問答部分
一兴喂、函數(shù)聲明和函數(shù)表達(dá)式有什么區(qū)別蔼囊?
- 二者表示函數(shù)的方式不一樣,如下
- 函數(shù)聲明(函數(shù)名稱不可少)
function funName(){
statement;
}
- 函數(shù)表達(dá)式(函數(shù)名稱可以沒有衣迷,末尾有一個(gè)分號)
var funName = function(){
statement;
};
- 二者在函數(shù)名提升的時(shí)候不一樣(函數(shù)名提升是指JS引擎在解析代碼時(shí)畏鼓,整個(gè)函數(shù)會像變量提升一樣提到代碼頭部,但此時(shí)并未執(zhí)行)
- 對于函數(shù)聲明蘑险,如下代碼
printName("jirengu");
function printName(name){
console.log(name);
}
此時(shí)顯示的結(jié)果如下圖所示
此時(shí)執(zhí)行調(diào)用函數(shù)時(shí)不會報(bào)錯(cuò)滴肿,因?yàn)楹瘮?shù)名的提升,整個(gè)函數(shù)被提升到代碼頭部
- 對于函數(shù)表達(dá)式佃迄,如下代碼
printName("jirengu");
var printName = function(name){
console.log(name);
}泼差;
此時(shí)顯示結(jié)果如下圖所示
這個(gè)時(shí)候會報(bào)錯(cuò)是因?yàn)榇藭r(shí)JS引擎是如下處理代碼的
var printName;
printName("jirengu");
printName = function(name){
console.log(name);
}贵少;
以上代碼中,第一行表示函數(shù)變量聲明提前堆缘,但還未被賦值滔灶,等于undefined;所以在執(zhí)行第二行時(shí)則會發(fā)生錯(cuò)誤吼肥;即函數(shù)表達(dá)式只是函數(shù)聲明提前录平,但是函數(shù)部分并未提前
二、什么是變量的聲明前置缀皱?什么是函數(shù)的聲明前置斗这?
- 變量的聲明前置是指Javascript引擎解析代碼,獲取所有被聲明的代碼變量啤斗,然后再一行一行的執(zhí)行表箭。這樣造成的結(jié)果是所有變量的聲明語句,都將被提升到代碼頭部钮莲,如下情況
console.log(a);
var a = 1;
此時(shí)并不會報(bào)錯(cuò)免钻,結(jié)果如下圖所示
JS引擎在解析代碼時(shí)相當(dāng)于以下過程
var a;//變量聲明提升到代碼頭部
console.log(a);
a = 1;
- 函數(shù)聲明前置指JS引擎在解析代碼時(shí),整個(gè)函數(shù)會像變量提升一樣提到代碼頭部崔拥,如下情況
printNumber(12);
function printNumber(num){
console.log(num);
}
此時(shí)不會報(bào)錯(cuò)极舔,顯示結(jié)果如下圖所示
此過程相當(dāng)于一下代碼
function printNumber(num){
console.log(num);
}//函數(shù)聲明前置會讓整個(gè)函數(shù)提到代碼頭部
printNumber(12);
還有函數(shù)表達(dá)式的前置,見問題1
三链瓦、arguments 是什么拆魏?
- arguments~函數(shù)運(yùn)行時(shí),有時(shí)需要提供外部數(shù)據(jù)慈俯,不同的外部數(shù)據(jù)會得到不同的結(jié)果稽揭,這種外部數(shù)據(jù)即為參數(shù),比如如下代碼
function getSquare(x){
return x * x;
}
console.log(getSquare(4));//16
其中x即為getSquare函數(shù)的參數(shù)肥卡,每次運(yùn)行g(shù)etSquare函數(shù)時(shí),都要提供x值事镣,否則不會得到結(jié)果
- arguments不是必須的步鉴,JS中允許省略arguments,被省略的參數(shù)的值就變?yōu)閡ndefined
- 在函數(shù)體內(nèi)可以通過arguments對象來訪問這個(gè)數(shù)組,從而獲取傳遞給函數(shù)的每一個(gè)參數(shù)璃哟,如下代碼
function visitArg(){
console.log(arguments[0]);
console.log(arguments[1]);
console.log(arguments[2]);
}
visitArg("ji","ren","gu");
顯示結(jié)果如下圖
- 通過訪問arguments對象的length屬性可以獲知有多少個(gè)參數(shù)傳遞給了函數(shù)氛琢,如下
function howmanyArg(){
console.log(arguments.length);
}
howmanyArg(1,2,3,4);
howmanyArg(12);
howmanyArg();
howmanyArg("hello",4,false);
輸出結(jié)果為下圖所示
四、函數(shù)的重載怎樣實(shí)現(xiàn)随闪?
- 在一些語言中(如Java)中阳似,相同名字的函數(shù)參數(shù)個(gè)數(shù)不同或者順序不同都被認(rèn)為是不同的函數(shù),稱為函數(shù)重載铐伴。但是在Javascript中沒有函數(shù)的重載
- 在Javascript中可以通過arguments來實(shí)現(xiàn)函數(shù)的重載撮奏,如下代碼
function sum(){
var x = 0;
for(var i = 0;i < arguments.length;i++){
x += arguments[i];
}
return x;
}
console.log(sum(1,2,3));//6
五俏讹、立即執(zhí)行函數(shù)表達(dá)式是什么?有什么作用 畜吊?
- 立即執(zhí)行函數(shù)模式(沒有聲明前置)是一種語法泽疆,可以讓你的函數(shù)在定義后立即被執(zhí)行,這種模式本質(zhì)上就是函數(shù)表達(dá)式(命名的或者匿名的)玲献,在創(chuàng)建后立即執(zhí)行殉疼,其代碼格式如下
//在最前最后加括號
(function atOnce(){
console.log("abc");
}());
//在function外面加括號
(function atOnce(){
console.log("abc");
})();
- 立即執(zhí)行函數(shù)表達(dá)式的作用:
- 立即執(zhí)行函數(shù)模式被廣泛使用,它可以幫你封裝大量的工作而不會在背后遺留任何全局變量
- 定義的所有變量都會成員立即執(zhí)行函數(shù)的局部變量捌年,所以你不用擔(dān)心這些臨時(shí)變量會污染全局空間
- 這種模式經(jīng)常被使用在書簽工具(bookmarklets)中瓢娜,因?yàn)闀灩ぞ咴谌魏雾撁嫔线\(yùn)行并且保持全局命名空間干凈是非常必要的
- 這種模式也可以讓你將獨(dú)立的功能封裝在自包含模塊中
更多知識
六、什么是函數(shù)的作用域鏈礼预?
- 函數(shù)的作用域鏈~任何程序設(shè)計(jì)語言都有作用域的概念眠砾,簡單的說,作用域就是變量與函數(shù)的可訪問范圍逆瑞,即作用域控制著變量與函數(shù)的可見性和生命周期荠藤。在JavaScript中,變量的作用域有全局作用域和局部作用域兩種
- 全局作用域~在代碼中任何地方都能訪問到的對象擁有全局作用域获高,一般來說以下幾種情形擁有全局作用域
- 最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域哈肖,如下
var num = 1;
function example(){
var ourAuthor = "jirengu";
function printName(){
console.log(ourAuthor);
}
printName();
}
//在這段代碼中,變量num和函數(shù)example是擁有全局作用域念秧;
而在函數(shù)example里面的變量和函數(shù)并不擁有全局作用域
- 所有末定義直接賦值的變量自動聲明為擁有全局作用域淤井,如下
function example(){
var a = "jirengu";
b ="quanju";
console.log(a);
}
//在以上代碼中,變量b擁有全局作用域摊趾;而變量a則不是
- 所有window對象的屬性擁有全局作用域
- 局部作用域~和全局作用域相反币狠,局部作用域一般只在固定的代碼片段內(nèi)可訪問到,最常見的例如函數(shù)內(nèi)部砾层,所有在一些地方也會看到有人把這種作用域稱為函數(shù)作用域
function example(){
var author = "jirengu";
function printName(){
console.log(ourAuthor);
}
printName();
}
//在以上代碼中漩绵,變量author和函數(shù)printName都只擁有局部變量
- 作用域鏈~在JavaScript中,函數(shù)也是對象肛炮,實(shí)際上止吐,JavaScript里一切都是對象。函數(shù)對象和其它對象一樣侨糟,擁有可以通過代碼訪問的屬性和一系列僅供JavaScript引擎訪問的內(nèi)部屬性碍扔。其中一個(gè)內(nèi)部屬性是[[Scope]],由ECMA-262標(biāo)準(zhǔn)第三版定義秕重,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合不同,這個(gè)集合被稱為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問(如果變量在該作用域中沒有,則它會逐級向上尋找二拐,直至最頂層)
- 以上內(nèi)容引用自JavaScript 開發(fā)進(jìn)階:理解 JavaScript 作用域和作用域鏈
代碼部分
一服鹅、以下代碼輸出什么?
function getInfo(name,age,sex){
console.log("name:",name);
console.log("age:",age);
console.log("sex:",sex);
console.log(arguments);
arguments[0] = "valley";
console.log("name",name);
}
getInfo("hunger",28,"男");
getInfo("hunger",28);
getInfo("男");
輸出的結(jié)果為
//getInfo("hunger",28,"男");輸出結(jié)果為
name:hunger
age:28
sex:男
["hunger",28,"男"]
name valley
//getInfo("hunger",28);輸出結(jié)果為
name:hunger
age:28
sex:undefined
["hunger",28]
name valley
//getInfo("男");輸出結(jié)果為
name:男
age:undefined
sex:undefined
["男"]
name valley
在chrome上運(yùn)行如下圖所示
二卓鹿、寫一個(gè)函數(shù)菱魔,返回參數(shù)的平方和?
function sumOfSquares(){
var s = 0;
for(var i = 0;i < arguments.length;i++){
s += arguments[i] * arguments[i];
}
console.log(s);
}
sumOfSquares(2,3,4);//29
sumOfSquares(1,3);//10
在chrome中一下效果圖
三吟孙、如下代碼的輸出澜倦?為什么?
console.log(a);//輸出結(jié)果為undefined
var a = 1;//輸出結(jié)果為1
console.log(b);// Uncaught ReferenceError: b is not defined
造成以上結(jié)果的原因是變量提升杰妓,即JS引擎在解析代碼時(shí)會把所有的變量聲明提到代碼頭部藻治,再一行一行的執(zhí)行代碼,所以第一行代碼輸出結(jié)果為undefined巷挥;而第三行代碼是因?yàn)閎并不是變量桩卵,所以會報(bào)錯(cuò);以上代碼相當(dāng)于以下過程
var a;//變量聲明提升
console.log(a);
a = 1;
console.log(b);
四倍宾、如下代碼的輸出雏节?為什么?
sayName('world');
sayAge(10);
function sayName(name){
console.log('hello ', name);
}
var sayAge = function(age){
console.log(age);
};
以上代碼的輸出結(jié)果為
//sayName("world");輸出結(jié)果為hello world
//sayAge(10);輸出結(jié)果為Uncaught TypeError: sayAge is not a function
第一行代碼由于函數(shù)聲明提升高职,整個(gè)sayName函數(shù)提到代碼頭部钩乍,則在執(zhí)行sayName("world")時(shí)輸出正常結(jié)果;第二行代碼由于sayAge是函數(shù)表達(dá)式怔锌,則js引擎在解析代碼時(shí)寥粹,只是把var sayAge提到代碼頭部,并未把整個(gè)函數(shù)部分提到代碼頭部埃元,所以在執(zhí)行時(shí)會報(bào)錯(cuò)涝涤,以上內(nèi)容相當(dāng)于以下過程
function sayName(name){
console.log('hello ', name);
}//函數(shù)聲明前置將整個(gè)函數(shù)提到代碼頭部
var sayAge;//函數(shù)表達(dá)式只是把var sayAge提到代碼頭部
sayName('world');//輸出結(jié)果為hello world
sayAge(10);//sayAge并不是一個(gè)函數(shù),則會報(bào)錯(cuò)
sayAge = function(age){
console.log(age);
};//將右邊的函數(shù)賦值給sayAge
五岛杀、如下代碼的輸出阔拳?為什么?
function fn(){}
var fn = 3;
console.log(fn);//輸出結(jié)果為3
由于變量聲明前置和函數(shù)聲明前置类嗤,以上代碼相當(dāng)于一下過程
var fn;//變量聲明前置衫生,將var fn提到代碼頭部
function fn(){}//函數(shù)聲明前置
fn = 3;//將數(shù)值3賦值給fn
console.log(fn);//最終輸出結(jié)果為3
六、如下代碼的輸出土浸?為什么?
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
fn(10);
最終出現(xiàn)的結(jié)果是
//fn(10);輸出結(jié)果為
function fn2(){
console.log('fnnn2');
}
3
function fn(fn2){
console.log(fn2);
var fn2 = 3;
console.log(fn2);
console.log(fn);
function fn2(){
console.log('fnnn2');
}
}
出現(xiàn)以上結(jié)果相當(dāng)于js引擎在解析代碼時(shí)做了以下事情
function fn(fn2){
var fn2;//變量聲明提升
function fn2(){
console.log('fnnn2');
}//函數(shù)聲明提升
console.log(fn2);//輸出結(jié)果為fn2函數(shù)
fn = 3;//fn被重新賦值
console.log(fn2);//輸出結(jié)果為3
console.log(fn);//輸出結(jié)果為fn函數(shù)
}
fn(10);
七、如下代碼的輸出彭羹?為什么黄伊?
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
以上代碼輸出結(jié)果為
//console.log(fn(fn));
Uncaught TypeError: fn is not a function
出現(xiàn)以上結(jié)果是因?yàn)镴S引擎在解析代碼時(shí)做了以下事情
var fn;//變量聲明提升
function fn(fn){
console.log(fn);
}//函數(shù)聲明提升
fn = 1;//把數(shù)值1賦值給函數(shù)fn
console.log(fn(fn));//會報(bào)錯(cuò),因?yàn)閒n不是函數(shù)派殷,已經(jīng)被賦值為1
八还最、如下代碼的輸出墓阀?為什么?
//作用域
console.log(j);
console.log(i);
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);
console.log(j);
最終輸出結(jié)果為
console.log(i);//輸出結(jié)果為10
console.log(j);//輸出結(jié)果為100
相當(dāng)于以下過程
var i;//變量提升拓轻,將var i提到代碼頭部
var j;//變量提升斯撮,將var j也提到代碼頭部
console.log(i);//undefined,此時(shí)變量i還未賦值
console.log(j);//undefined扶叉,此時(shí)變量j也還未賦值
for(var i=0;i<10;i++){
var j = 100;
}
console.log(i);//在for循環(huán)里執(zhí)行完后勿锅,i為10
console.log(j)//在for循環(huán)執(zhí)行后,j被賦值為100
九枣氧、如下代碼的輸出溢十?為什么 ?
fn();
var i = 10;
var fn = 20;
console.log(i);
function fn(){
console.log(i);
var i = 99;
fn2();
console.log(i);
function fn2(){
i = 100;
}
}
最終輸出結(jié)果為
undefined
100
10
相當(dāng)于以下過程
var i;//變量提升达吞,將var i提到代碼頭部
var fn;//變量提升张弛,將var i提到代碼頭部
function fn(){
var i;//函數(shù)體內(nèi)部的變量提升
function fn2(){
i = 100;
}//函數(shù)體內(nèi)部的函數(shù)聲明提升
console.log(i);//undefined,因?yàn)榇藭r(shí)的i只是聲明酪劫,但是還未賦值
i = 99;//把99賦值給變量i
fn2();//執(zhí)行fn2函數(shù)吞鸭,執(zhí)行完后結(jié)果是i的值為100
console.log(i);//100
}//函數(shù)聲明提升,將整個(gè)函數(shù)提到代碼頭部
fn();//執(zhí)行函數(shù)fn覆糟,執(zhí)行完后刻剥,得到undefined和100l兩個(gè)結(jié)果
i = 10;//把10賦值給ifn = 20;//把20賦值給fn
console.log(i);//10
十、如下代碼的輸出搪桂?為什么透敌?
var say = 0;
(function say(n){
console.log(n);
if(n<3) return;
say(n-1);
}( 10 ));
console.log(say);
最終輸出結(jié)果如下圖
- 首先,立即執(zhí)行函數(shù)時(shí)沒有函數(shù)聲明前置的踢械,則以上代碼會按照順序來執(zhí)行酗电,解釋如下
var say = 0;//把0賦值給變量say
(function say(n){
console.log(n);//輸出給定的參數(shù)n~10,9,8,7,6,5,4,3,2
if(n<3) return;//當(dāng)n<3時(shí),跳出函數(shù)内列,故當(dāng)n為2時(shí)跳出函數(shù)
say(n-1);
}( 10 ));
console.log(say);//輸出為0撵术,因?yàn)榱⒓磮?zhí)行函數(shù)執(zhí)行完了就完了,這里是變量say
版權(quán)聲明:本教程版權(quán)歸鄧攀和饑人谷所有话瞧,轉(zhuǎn)載須說明來源D塾搿!=慌拧划滋!