問答
函數聲明和函數表達式有什么區別 (*)
函數聲明形式:
function sayHello(){
console.log("Hello, World! ");
}
函數表達式形式:
var sayHello = function(){
console.log("Hello, World! ");
};
在聲明一個變量的時候,javascript解釋器會將變量聲明的語句提前,函數聲明形式會發生函數聲明前置,所以代碼如果放在使用之后也可以生效,而函數表達式形式只發生了變量聲明前置,如果在定義之前使用,函數名目前還只是一個變量名,不能用()進行執行,會拋出錯誤。
什么是變量的聲明前置?什么是函數的聲明前置 (**)
var a = 1;
//實際上相當于:
var a; //這句聲明會提前到所有語句之前執行
a=1;
所以,
console.log(b);
var b = 1;//console.log 并不會報錯,而是返回變量聲明之后的值undefined。
arguments 是什么 (*)
arguments 是一個可以接收所有函數傳遞參數的類似數組,在函數里可以直接取用。當函數的參數不確定數量的時候可以使用arguments對形參進行數組的方式操作,可以實現類似重載。
函數的重載怎樣實現 (**)
function fn(){
var sum = 0;
for(x in arguments ){
sum=arguments[x]+sum;
};
console.log(sum);
}
//無論多少參數都可實現累加運算
立即執行函數表達式是什么?有什么作用 (***)
(function() {
c =1 ;
console.log(c)
})();
立即執行函數可以將寫在函數體內的語句直接執行。區別于普通語句,立即執行函數內的變量不會干擾函數體外,形成一個類似區塊的空間。
什么是函數的作用域鏈 (****)
函數對象其中一個內部屬性是[[Scope]],由ECMA-262標準第三版定義,該內部屬性包含了函數被創建的作用域中對象的集合,這個集合被稱為函數的作用域鏈,它決定了哪些數據能被函數訪問。
在函數執行過程中,每遇到一個變量,都會經歷一次標識符解析過程以決定從哪里獲取和存儲數據。該過程從作用域鏈頭部,也就是從活動對象開始搜索,查找同名的標識符,如果找到了就使用這個標識符對應的變量,如果沒找到繼續搜索作用域鏈中的下一個對象,如果搜索完所有對象都未找到,則認為該標識符未定義。函數執行過程中,每個標識符都要經歷這樣的搜索過程。
--JavaScript 開發進階:理解 JavaScript 作用域和作用域鏈]
簡單來說,函數作用域鏈用于查找函數內部變量的位置。在執行過程中,按照作用域鏈表的順序依次進行查找。在函數每執行一次,這個表進行動態創建。所以,在實際使用中應當盡量減少全局變量的使用,避免with語句使用,如果一個跨作用域的對象被引用了一次以上,則先把它存儲到局部變量里再使用。
代碼
- 以下代碼輸出什么? (難度**)
- 可以看出arguments 是參數的數組,可以對其進行修改,改變參數。
- 當參數缺省的時候,定義為 undefined。
- 參數是具有順序的,第一個參數傳遞為第一個形參。
- 寫一個函數,返回參數的平方和?如 (難度**)
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);//報錯 提示未定義
因為聲明前置,實際上在這段代碼執行前就已經執行了var a;
,所以在console.log(a)
的時候不發生錯誤,返回undefined。
4.如下代碼的輸出?為什么 (難度*)
- 由于函數聲明前置,sayName可以正常執行
- sayAge用了函數表達式形式,在賦值之前都不可以使用。
5.如下代碼的輸出?為什么 (難度**)
function fn(){}
var fn = 3;
console.log(fn);//3
結果比較直觀,實際上將上面的語句顛倒也會輸出相同的結果:
var fn = 3;
function fn(){}
console.log(fn);//3
- 還是因為聲明前置的原因,可以將聲明都放到開頭,再來理解:
var fn;
function fn(){}
fn = 3;
console.log(fn);//3
這就是上面那段代碼實際執行的樣子,這也就不難理解了。
需要注意的是,聲明前置,變量的聲明要比函數的聲明要更早執行。
function fn(){}
var fn;
console.log(fn);
//function fn(){}
6.如下代碼的輸出?為什么 (難度***)
- 在函數內部定義的函數,也會發生聲明前置。上圖中即使傳入了參數10,但與函數內部函數重名,被覆蓋掉了。此時console.log(fn2),打印出的就是fn2函數本身。此后fn2被重新賦值2,打印出3。
7.如下代碼的輸出?為什么 (難度***)
var fn = 1;
function fn(fn){
console.log(fn);
}
console.log(fn(fn));
//Uncaught TypeError: fn is not a function(…)
實際執行過程,由于聲明前置:
var fn;
function fn(fn){
console.log(fn);
}
fn = 1;
console.log(fn(fn));
fn被重新賦值為1,自然無法執行,報錯。
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是屬于運算符,不屬于函數,所以在其內部定義的變量和當前屬于同一個作用域。在JS中只有函數內部使用var定義,才具備另一個作用域性質。
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.如下代碼的輸出?為什么 (難度*****)
- 立即執行函數的內部定義的函數名、變量不會影響到函數外面。所以在其內部執行的say(),并沒有覆蓋函數外的say = 0; 在最后打印出來還是0 。
- 函數內部有一個嵌套結構。使用嵌套時,函數名可以在function后定義。不需要的時候可以不加。
- return的執行,伴隨函數的退出,不再繼續往下執行。
本教程版權歸屬于 張宇 及 饑人谷 所有,轉載請說明來源~