問答
函數(shù)聲明和函數(shù)表達式有什么區(qū)別 (*)
函數(shù)聲明形式:
function sayHello(){
console.log("Hello, World! ");
}
函數(shù)表達式形式:
var sayHello = function(){
console.log("Hello, World! ");
};
在聲明一個變量的時候,javascript解釋器會將變量聲明的語句提前,函數(shù)聲明形式會發(fā)生函數(shù)聲明前置,所以代碼如果放在使用之后也可以生效,而函數(shù)表達式形式只發(fā)生了變量聲明前置,如果在定義之前使用,函數(shù)名目前還只是一個變量名,不能用()進行執(zhí)行,會拋出錯誤。
什么是變量的聲明前置?什么是函數(shù)的聲明前置 (**)
var a = 1;
//實際上相當(dāng)于:
var a; //這句聲明會提前到所有語句之前執(zhí)行
a=1;
所以,
console.log(b);
var b = 1;//console.log 并不會報錯,而是返回變量聲明之后的值undefined。
arguments 是什么 (*)
arguments 是一個可以接收所有函數(shù)傳遞參數(shù)的類似數(shù)組,在函數(shù)里可以直接取用。當(dāng)函數(shù)的參數(shù)不確定數(shù)量的時候可以使用arguments對形參進行數(shù)組的方式操作,可以實現(xiàn)類似重載。
函數(shù)的重載怎樣實現(xiàn) (**)
function fn(){
var sum = 0;
for(x in arguments ){
sum=arguments[x]+sum;
};
console.log(sum);
}
//無論多少參數(shù)都可實現(xiàn)累加運算
立即執(zhí)行函數(shù)表達式是什么?有什么作用 (***)
(function() {
c =1 ;
console.log(c)
})();
立即執(zhí)行函數(shù)可以將寫在函數(shù)體內(nèi)的語句直接執(zhí)行。區(qū)別于普通語句,立即執(zhí)行函數(shù)內(nèi)的變量不會干擾函數(shù)體外,形成一個類似區(qū)塊的空間。
什么是函數(shù)的作用域鏈 (****)
函數(shù)對象其中一個內(nèi)部屬性是[[Scope]],由ECMA-262標(biāo)準(zhǔn)第三版定義,該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對象的集合,這個集合被稱為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問。
在函數(shù)執(zhí)行過程中,每遇到一個變量,都會經(jīng)歷一次標(biāo)識符解析過程以決定從哪里獲取和存儲數(shù)據(jù)。該過程從作用域鏈頭部,也就是從活動對象開始搜索,查找同名的標(biāo)識符,如果找到了就使用這個標(biāo)識符對應(yīng)的變量,如果沒找到繼續(xù)搜索作用域鏈中的下一個對象,如果搜索完所有對象都未找到,則認(rèn)為該標(biāo)識符未定義。函數(shù)執(zhí)行過程中,每個標(biāo)識符都要經(jīng)歷這樣的搜索過程。
--JavaScript 開發(fā)進階:理解 JavaScript 作用域和作用域鏈]
簡單來說,函數(shù)作用域鏈用于查找函數(shù)內(nèi)部變量的位置。在執(zhí)行過程中,按照作用域鏈表的順序依次進行查找。在函數(shù)每執(zhí)行一次,這個表進行動態(tài)創(chuàng)建。所以,在實際使用中應(yīng)當(dāng)盡量減少全局變量的使用,避免with語句使用,如果一個跨作用域的對象被引用了一次以上,則先把它存儲到局部變量里再使用。
代碼
- 以下代碼輸出什么? (難度**)

- 可以看出arguments 是參數(shù)的數(shù)組,可以對其進行修改,改變參數(shù)。
- 當(dāng)參數(shù)缺省的時候,定義為 undefined。
- 參數(shù)是具有順序的,第一個參數(shù)傳遞為第一個形參。
- 寫一個函數(shù),返回參數(shù)的平方和?如 (難度**)
function sumOfSquares(){
var sum = 0;
for(var x in arguments){
sum += arguments[x]*arguments[x];
}
console.log(sum);
}
sumOfSquares(2,3,4); // 29
sumOfSquares(1,3); // 10
- 如下代碼的輸出?為什么 (難度*)
console.log(a);//返回 undefined
var a = 1;
console.log(b);//報錯 提示未定義
因為聲明前置,實際上在這段代碼執(zhí)行前就已經(jīng)執(zhí)行了var a;,所以在console.log(a)的時候不發(fā)生錯誤,返回undefined。
4.如下代碼的輸出?為什么 (難度*)

- 由于函數(shù)聲明前置,sayName可以正常執(zhí)行
- sayAge用了函數(shù)表達式形式,在賦值之前都不可以使用。
5.如下代碼的輸出?為什么 (難度**)
function fn(){}
var fn = 3;
console.log(fn);//3
結(jié)果比較直觀,實際上將上面的語句顛倒也會輸出相同的結(jié)果:
var fn = 3;
function fn(){}
console.log(fn);//3
- 還是因為聲明前置的原因,可以將聲明都放到開頭,再來理解:
var fn;
function fn(){}
fn = 3;
console.log(fn);//3
這就是上面那段代碼實際執(zhí)行的樣子,這也就不難理解了。
需要注意的是,聲明前置,變量的聲明要比函數(shù)的聲明要更早執(zhí)行。
function fn(){}
var fn;
console.log(fn);
//function fn(){}
6.如下代碼的輸出?為什么 (難度***)

- 在函數(shù)內(nèi)部定義的函數(shù),也會發(fā)生聲明前置。上圖中即使傳入了參數(shù)10,但與函數(shù)內(nèi)部函數(shù)重名,被覆蓋掉了。此時console.log(fn2),打印出的就是fn2函數(shù)本身。此后fn2被重新賦值2,打印出3。
7.如下代碼的輸出?為什么 (難度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
//Uncaught TypeError: fn is not a function(…)
實際執(zhí)行過程,由于聲明前置:
var fn;
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn));
fn被重新賦值為1,自然無法執(zhí)行,報錯。
8.如下代碼的輸出?為什么 (難度**)
//作用域
console.log(j);//undefined
console.log(i);//undefined
for(var i=0; i<10; i++){
var j = 100;
}
console.log(i);//10
console.log(j);//100
- 需要注意的是,for是屬于運算符,不屬于函數(shù),所以在其內(nèi)部定義的變量和當(dāng)前屬于同一個作用域。在JS中只有函數(shù)內(nèi)部使用var定義,才具備另一個作用域性質(zhì)。
9.如下代碼的輸出?為什么 (難度****)
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;
}
}
改寫代碼:
var i;
var fn;
function fn(){
var i;
function fn2(){
i = 100;
}
console.log(i);//undefined
i = 99;
fn2();
console.log(i);//100
}
fn();
//undefined
//100
i = 10;
fn = 20;
console.log(i);//10
10.如下代碼的輸出?為什么 (難度*****)

- 立即執(zhí)行函數(shù)的內(nèi)部定義的函數(shù)名、變量不會影響到函數(shù)外面。所以在其內(nèi)部執(zhí)行的say(),并沒有覆蓋函數(shù)外的say = 0; 在最后打印出來還是0 。
- 函數(shù)內(nèi)部有一個嵌套結(jié)構(gòu)。使用嵌套時,函數(shù)名可以在function后定義。不需要的時候可以不加。
- return的執(zhí)行,伴隨函數(shù)的退出,不再繼續(xù)往下執(zhí)行。
本教程版權(quán)歸屬于 張宇 及 饑人谷 所有,轉(zhuǎn)載請說明來源~
