這是一個非常重要的知識點了,了解了JavaScript的作用域鏈的話,能幫助我們理解很多‘異常'問題。
下面我們來看一個小例子,前面我說過的聲明提前的例子。
var name = 'Skylor.min'; function echo() { alert(name); var name = 'mm'; alert(name); alert(age); } echo();對于這個例子,沒有接觸過這方面的時候,第一反應是會糾結下,這第一個的name,到底調用全局變量的name,還是函數內部的name呢,如果調用全局的,可是函數內部也用定義和賦值啊, 如果調用函數內部的局部變量的話,那么他的值是mm嗎?還是引用全局的'Skylor.min'呢?
于是這個小例子就會有這樣的錯誤答案:
Skylor.min
mm
[腳本出錯]
其實不然,知道函數內的提前說明,就知道這是不正確的。
undefined
mm
[腳本出錯]
應該是這樣的,那到底為什么是這個答案呢,提前聲明這又是什么呢?一切的一切,涉及到JavaScript的作用域鏈。
首先來說說,JavaScript的作用域的原理:
在JavaScript權威指南中有一句很精辟的描述: JavaScript中的函數運行在它們被定義的作用域里,而不是它們被運行的作用域里。
另外在JavaScript中有個很重要的概念,那就是: 在JavaScript中,一切皆對象,函數也是。
在JS中,作用域的概念和其他語言差不多, 在每次調用一個函數的時候 ,就會進入一個函數內的作用域,當從函數返回以后,就返回調用前的作用域
JS的語法風格和C/C++類似, 但作用域的實現卻和C/C++不同,并非用“堆棧”方式,而是使用列表,具體過程如下(ECMA262中所述):
任何執行上下文時刻的作用域, 都是由作用域鏈(scope chain, 后面介紹)來實現 在一個函數被定義的時候, 會將它定義時刻的scope chain鏈接到這個函數對象的[[scope]]屬性 在一個函數對象被調用的時候,會創建一個活動對象(也就是一個對象), 然后對于每一個函數的形參,都命名為該活動對象的命名屬性, 然后將這個活動對象做為此時的作用域鏈(scope chain)最前端, 并將這個函數對象的[[scope]]加入到scope chain中.看個例子吧:
var func = function(lps, rps){
var name = 'Skylor.min';
........
}
func();
在執行func的定義語句的時候, 會創建一個這個函數對象的[[scope]]屬性(內部屬性,只有JS引擎可以訪問, 但FireFox的幾個引擎(SpiderMonkey和Rhino)提供了私有屬性__parent__來訪問它), 并將這個[[scope]]屬性, 鏈接到定義它的作用域鏈上(后面會詳細介紹), 此時因為func定義在全局環境, 所以此時的[[scope]]只是指向全局活動對象window active object.
新聞熱點
疑難解答
圖片精選