JavaScript 作用域鏈、閉包、原型和原型鏈

作用域鏈及和標識符解析

每一個JavaScript函數都表示爲一個對象, 要確切的說, 是Function對象的一個實例. Function對象同其他對象一樣, 擁有可以編程訪問的屬性, 和一系列不能通過代碼訪問而僅供JavaScript引擎存取的內部屬性. 其中有一個內部屬性是[[Scope]].

內部屬性[[Scope]]包含了一個函數被創建的作用域中對象的集合。這個集合被稱爲函數的作用域鏈。它決定哪些數據將被函數訪問。函數作用域中每一個對象被稱爲一個可變對象.。每個可變對象都以”鍵值對”的形式存在. 當一個函數創建後, 它的作用域鏈會被創建此函數的作用域中可訪問的數據對象所填充。例如,思考下面的全局函數:

function add(num1,num2){
     var sum = num1 + num2;
     return sum;
}

當函數add()創建時,它的作用域插入了一個對象變量,這個全局對象代表着所有在全局範圍內定義的變量。該全局對象包含諸如window、navigator和document等。如下圖所示:

函數add的作用域將會在執行時期用到.。假設執行如下代碼:

var total = add(5,10);

執行此函數時會創建一個稱爲執行環境(execution context)的內部對象, 一個執行環境定義了一個函數執行時的環境, 函數每次執行環境都是獨一無二的, 所以多次調用同一個函數就會導致創建多個執行環境. 當函數執行完畢, 執行環境就會被銷燬。
每個執行環境都有自己的作用域鏈, 用於解析標識符。當執行環境被創建時, 它的作用域鏈初始化爲當前被運行函數的[[Scope]]屬性中的對象。這些值按照它們出現的順序被複制到執行環境中. 這個過程一旦完成, 一個被稱爲”活動對象”的新對象就爲執行環境創建好了。活動對象作爲函數運行時的變量對象。包含所有局部變量, 命名參數, 參數集合以及this。然後此對象被推入作用域鏈的最前端.。當執行環境被銷燬, 活動對象也隨之被銷燬。

在函數執行過程中, 每遇到一個變量, 都會經歷一次標識符解析過程以決定從哪裏獲取或存儲數據, 該過程搜索執行環境的作用域鏈, 查找同名的標識符, 搜索過程從作用域鏈頭部開始, 也就是當前運行函數的活動對象. 如果找到, 就使用這個標識符對應的變量, 如果沒有找到, 繼續搜索作用域鏈中的下一個對象, 那麼標識符被視爲是未定義的. 在函數執行過程中, 每個標識符都要經歷這樣的搜索過程. 在前面的代碼示例中, 函數訪問sum, num1, num2時都會產生搜索過程, 正是這個搜索過程影響了性能。

一個好的經驗法則是: 如果某個跨作用域的值被引用一次以上, 那麼就把它儲存到局部變量. 例如先將全局變量的引用存儲在一個局部變量中, 然後使用這個局部變量代替全局變量.

閉包、作用域和內存

 

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