微信小遊戲學習--那些被你忽視掉的JavaScript作用域和作用域鏈

很多人在使用JavaScript的時候都會遇到一些奇葩的問題,而其中不少問題是因爲大家忽視掉了JavaScript中作用域與作用域鏈相關知識。推薦個一學習交流羣:1072209430

一、 JavaScript作用域

在 JavaScript中,只有局部作用域和全局作用域。而只有函數可以創建局部作用域,像 if,for 或者 while 這種塊語句是沒辦法創建作用域的。 (當然 ES6 提供了 let 關鍵字可以創建塊作用域。)

JavaScript的這種特性導致 for 循環裏面創建閉包時會產生讓人意想不到的結果。比如下面這個例子:

上面的輸出結果,大致原因就是 for 循環裏面的變量的作用域是整個函數的,循環內部創建的一系列閉包引用的是同一個變量 i,而在 for 循環結束後,這個 i 的值變成了 10。所以當我們調用這些內部函數的時候,就會輸出 10 了。

現在這樣講可能還是不夠清楚,在我們瞭解作用域鏈和 JavaScript的執行原理後,就更容易理解了。

二、 JavaScript作用域鏈

當JS 裏面 聲明 一個函數的時候,會給該函數對象創建一個 scope 屬性,該屬性指向當前作用域鏈對象。

當JS裏面 調用 一個函數的時候,會創建一個執行上下文,這個執行上下文定義了函數解釋執行時的環境信息。每個執行上下文都有自己的作用域鏈,主要用於變量標識符的解析。

在JS引擎運行一個函數的時候,它首先會把該函數的 scope 屬性添加到執行上下文的作用域鏈上面,然後再創建一個 活動對象 添加到此作用域頂端共同組成了新的作用域鏈。活動對象包含了該函數的所有的形參,arguments 對象,所有的局變變量等信息。

當解釋執行函數的每一條語句的時,會依據這個執行上下文的作用域鏈來查找標識符,如果在一個作用域對象上面沒有找到標識符,則會沿着作用鏈一直向上查找,這一點類似於 JS 的原型繼承的屬性查找機制。

來看幾個具體的例子:

三、 變量提升

調用 echo 函數的第一行 name = "hello"時並不是對全局變量 name 進行重新賦值,而是對函數內部聲明的變量 name 進行賦值。所以,在 echo 函數聲明之後,調用 console.log(name)輸出的還是 mowan。

echo 函數內部的 name 變量“使用在前,而聲明在後”,這就是所謂的變量提升

正因爲函數內部的變量聲明會發生“提升”副作用,所以,最好的做法就是把函數需要用到的局部變量都放在函數開頭進行聲明,避免產生不必要的混淆。

四、 小結

JavaScript中的函數運行在它們被定義的作用域裏,而不是它們被執行的作用域裏。理解作用域和作用域鏈對於理解閉包和變量提升這種奇葩特性非常有幫助。

本文可能有些地方講的還不是非常清楚,讀者可以讀一讀後面的參考鏈接,相信會有助於理解。

http://www.cnblogs.com/lhb25/archive/2011/09/06/javascript-scope-chain.html

理解 JavaScript作用域鏈原理

http://code.tutsplus.com/tutorials/javascript-hoisting-explained--net-15092

http://dmitrysoshnikov.com/ecmascript/javascript-the-core/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章