記一個面試題引發的思考

王振宇發表於2019-03-14
前兩天群裡分享了一個面試題如下:

let foo = function(){
  console.log(1)
} 
(function foo(){
  foo = 10;
  console.log(foo); 
}());
console.log(foo); 複製程式碼

不知道你第一次想到的答案對不對呢?

輸出結果為:

f foo(){
  foo = 10;
  console.log(foo);
}
1
undefined複製程式碼

我們從後往前說這樣可能更好理解一點

首先undefined對應的是console.log(foo);

之所以是undefined是因為下圖所示位置

記一個面試題引發的思考

沒有分號導致後邊的()把前邊的匿名函式執行了,而如果函式沒有返回值的話,預設返回undefined,然後undefined被賦給了let宣告的變數foo,基於此,也就理解了為什麼1會被列印出來。

接下來就講到了重頭戲,也就是自執行函式中的console.log(foo)為什麼列印出來是函式體而不是10呢?

而在講解這一部分之前,我們先看這樣一段程式碼:

let myBar = function bar(){
  bar = 20;
  console.log('bar',bar);
  console.log('myBar',myBar);
}
myBar();
console.log(myBar);
console.log(bar);
複製程式碼

猜猜這段程式碼會輸出什麼呢?結果如下:

bar ƒ bar(){
  bar = 20;
  console.log('bar',bar);
  console.log('myBar',myBar);
}
myBar ƒ bar(){
  bar = 20;
  console.log('bar',bar);
  console.log('myBar',myBar);
}
ƒ bar(){
  bar = 20;
  console.log('bar',bar);
  console.log('myBar',myBar);
}
Uncaught ReferenceError: bar is not defined
複製程式碼

不難看出,bar內部可以訪問到bar和myBar,但是外面是訪問不到bar的,而且bar內部對bar的重新賦值也是沒有生效的。

由此我們得到結論,函式表示式宣告的函式,函式外部是無法訪問的,而在函式內部是隻讀的,在非嚴格模式下,函式內部的賦值靜默失敗,而在嚴格模式下會報錯

Uncaught TypeError: Assignment to Constant variable複製程式碼

接下來我們再延伸一下,請看如下程式碼:

let foo = function(){console.log(1)}
(function(){
  foo = 10;
  console.log(foo);
}());
複製程式碼

這段程式碼執行會輸出什麼呢?結果報錯如下:

Uncaught ReferenceError: foo is not defined
複製程式碼

有疑問的朋友可能會問,怎麼是not defined呢,明明我在自執行函式裡邊有宣告啊?

這是因為用let宣告的變數,不存在變數提升。而且要求必須 等let宣告語句執行完之後,變數才能使用,不然會報Uncaught ReferenceError錯誤。

ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令宣告的變數,從一開始就形成了封閉作用域。凡是在宣告之前就使用這些變數,就會報錯。總之,在程式碼塊內,使用let命令宣告變數之前,該變數都是不可用的。這在語法上,稱為“暫時性死區”(temporal dead zone,簡稱 TDZ)。作為引數的自執行函式先執行,而此時foo處於暫時性死區,所以報錯了。

如果有錯誤或者不嚴謹的地方,請給予指正,十分感謝!


相關文章