由此可見,函數的作用域鏈是創建函數的時候創建的。
執行上下文(Execute context )
函數add的作用域將會在執行時用到,例如:
復制代碼 代碼如下:
var total = add(5,10);
當執行 add 函數的時候, JavaScript 會創建一個 Execute context (執行上下文),執行上下文中就包含了 add 函數運行期所需要的所有信息。 Execute context 也有自己的 Scope chain, 當函數運行時, JavaScript 引擎會首先從用 add 函數的作用域鏈來初始化執行上下文的作用域鏈。
活動對象(Active Object)
然後 JavaScript 引擎又會創建一個 Active Object, 這些值按照它們出現在函數中的順序被復制到運行期上下文的作用域鏈中,它們共同組成了一個新的對象——“活動對象(activation object)”,這個對象裡面包含了函數運行期的所有局部變量,參數以及 this 等變量,此對象會被推入作用域鏈的前端,當運行期上下文被銷毀,活動對象也隨之銷毀。新的作用域鏈如下圖所示:
執行上下文是一個動態的概念,當函數運行的時候創建,活動對象 Active Object 也是一個動態的概念,它是被執行上下文的作用域鏈引用的,可以得出結論:執行上下文和活動對象都是動態概念,並且執行上下文的作用域鏈是由函數作用域鏈初始化的。
在函數執行過程中,每遇到一個變量,都會檢索從哪裡獲取和存儲數據,該過程從作用域鏈頭部,也就是從活動對象開始搜索,查找同名的標識符,如果找到了就使用這個標識符對應的變量,如果沒有則繼續搜索作用域鏈中的下一個對象,如果搜索完所有對象都未找到,則認為該標識符未定義,函數執行過程中,每個標識符都要經歷這樣的搜索過程。
閉包(closure)
先來看一個實例,javascript代碼:
復制代碼 代碼如下:
<script type="text/javascript">
function newLoad(){ //新建頁面加載的事件
for (var i = 1; i <=3; i++) {
var anchor = document.getElementById("anchor" + i); //找到每個anchor
anchor.onclick = function () {//為anchor添加單擊事件
alert ("you clicked anchor"+i);//給出點擊反應
}
}
}
window.onload = newLoad; //把newload事件賦值給頁面加載
</script>
前台代碼:
復制代碼 代碼如下:
<body>
<a id="anchor1" href="#">anchor1</a><br/>
<a id="anchor2" href="#">anchor2</a><br/>
<a id="anchor3" href="#">anchor3</a><br/>
</body>
運行結果:無論點擊那個anchor,總會彈出anchor4,而我們根本就沒有anchor4:
當我們加載頁面時,javascript中的newLoad函數已經運行完畢,由其中的循環可知,anchor已經賦值為4。但是由以前的編程經驗來看,局部變量使用完畢就會銷毀,但是anchor卻沒有,顯然這裡 JavaScript 采用了另外的方式。
閉包在 JavaScript 其實就是一個函數,在函數運行期被創建的,當上面的 函數被執行的時候,會創建一個閉包,而這個閉包會引用newLoad 作用域中的anchor。下面就來看看 JavaScript 是如何來實現閉包的:當執行 newLoad 函數的時候, JavaScript 引擎會創建newLoad函數執行上下文的作用域鏈,這個作用域鏈包含了 newLoad執行時的活動對象,同時 JavaScript 引擎也會創建一個閉包,而閉包的作用域鏈也會引用 newload的活動對象,這樣當 newload執行完的時候,雖然其執行上下文和活動對象都已經釋放了anchor,但是閉包還是引用著 newload 的活動對象,所以點擊顯示的是“you clicked anchor4”。運行期如圖:
閉包優化
既然閉包出現了我們不想看到的結果,我們需要優化它。優化後的javascript(其他不變):
復制代碼 代碼如下:
<script type="text/javascript">
function newLoad() { //新建頁面加載的事件
for (var i = 1; i <= 3; i++) {
var anchor = document.getElementById("anchor" + i); //找到每個anchor
listener(anchor,i);//listener函數
}
}
function listener(anchor, i) {
anchor.onclick = function () {//為anchor添加單擊事件
alert("you clicked anchor" + i); //給出點擊反應
}
}
window.onload = newLoad; //把newload事件賦值給頁面加載
</script>
運行結果:提示對應的提示信息
結果分析:優化後的結果是因為,我們把聲明的變量和單擊事件相分離。用以上解釋的作用域鏈解釋:頁面加載,先執行listener函數,而listener函數需要anchor變量,在listener函數作用域鏈中沒有,會進入下一級作用域鏈,即查找newLoad中的anchor,因為在listener中已經確定了哪個anchor單擊對應哪個提示信息,所以只是在newload中查找對應的anchor而已,不會等循環完畢產生anchor4。
因為接觸javascript時間尚短,理解有誤的地方歡迎指正。