神奇的函數做用域

前言

今天發現了兩個關於函數做用域的神奇例子,這裏和你們分享分享:javascript

第一個例子

    var a = 1; function foo() { if (!a) { var a = 2; } alert(a); }; foo();

上面這段代碼在運行時會產生什麼結果?java

 咱們來分析一下:es6

        1.建立了全局變量 a,定義其值爲 1
        2.建立了函數 foo
        3.在 foo 的函數體內,if 語句將不會執行,由於 !a 會將變量 a 轉變成布爾的假值,也就是 false
        4.跳過條件分支,alert 變量 a,最終的結果應該是輸出 1 閉包

看起來無懈可擊的分析,可是實際上,結果錯誤。答案居然是 2!爲何?函數

什麼叫申明?spa

        是指你聲稱某樣東西的存在,好比一個變量或一個函數;但你沒有說明這樣東西究竟是什麼,僅僅是告訴解釋器這樣東西存在而已;code

什麼叫定義?blog

        是指你指明瞭某樣東西的具體實現,好比一個變量的值是多少,一個函數的函數體是什麼,確切的表達了這樣東西的意義。ip

因此上面的代碼實際上能夠寫成這樣:作用域

    var a; a = 1; function foo() { var a;    // 關鍵在這裏
      if (!a) { a = 2; } alert(a); // 此時的 a 並不是函數體外的那個全局變量
 } foo();

而後又有人會問,不是有個if嗎?if不成立哪就不會爲a賦值爲2。

由於 JavaScript 沒有塊級做用域(Block Scoping),只有函數做用域(Function Scoping),因此說不是看見一對花括號 {} 就表明產生了新的做用域,和 C 不同!

當解析器讀到 if 語句的時候,它發現此處有一個變量聲明和賦值,因而解析器會將其聲明提高至當前做用域的頂部(這是默認行爲,而且沒法更改),這個行爲就叫作 Hoisting。

怎樣可以alert出那個a=1?

 let a; a = 1; function foo() { let a; // 關鍵在這裏
      if (!a) { a = 2; } alert(a); // 此時的 a 並不是函數體外的那個全局變量
 } foo();

es6的語法,javascript是有塊級做用域的。

還能夠經過閉包的方式實現:

  var a = 1; function foo() { if (!a) { (function() { var a = 2; }()); }; alert(a); }; foo();

第二個例子

 var a = 1; function test() { foo(); var foo = function() { alert(a); } } test();

這個運行的結果是什麼?初略一看,alert(1),可是實際上報錯。

  Uncaught TypeError: foo is not a function。

爲何會這樣?

  提高的僅僅是變量名 foo,至於它的定義依然停留在原處。所以在執行 foo() 以前,做用域只知道 foo 的命名,不知道它究竟是什麼,因此執行會報錯(一般會是:undefined is not a function)。這叫作函數表達式(Function Expression),函數表達式只有命名會被提高,定義的函數體則不會。

 var a = 1; function test() { var foo; foo(); // 這個時候函數foo,只聲明,未賦值。
        foo = function() { alert(a); } } test();

怎麼改?   

 var a = 1; function test() { var foo = function() { alert(a); } foo(); } test(); // 1

這個例子也展現了函數聲明與函數表達式的差異,函數申明會放到做用域的頂部,函數表達式則不會。

最後引用不少書中的一句話:「請始終保持做用域內全部變量的聲明放置在做用域的頂部」,相信如今的你對這句話應該有一個認識了。

相關文章
相關標籤/搜索