理解Javascript_15_作用域分配與變量訪問規(guī)則,再送個閉包
更新時間:2010年10月20日 11:24:53 作者:
在閱讀本博文之前,請先閱讀《理解Javascript_13_執(zhí)行模型詳解》
在'執(zhí)行模型詳解'中講到了關(guān)于作用域分配的問題,這一篇博文將詳細(xì)的說明函數(shù)對象、作用域鏈與執(zhí)行上下文的關(guān)系。
作用域分配與變量訪問規(guī)則
在 ECMAScript 中,函數(shù)也是對象。函數(shù)對象在變量實(shí)例化過程中會根據(jù)函數(shù)聲明來創(chuàng)建,或者是在計(jì)算函數(shù)表達(dá)式或調(diào)用 Function 構(gòu)造函數(shù)時創(chuàng)建。(關(guān)于'函數(shù)對象'請見《理解Javascript_08_函數(shù)對象》)。每個函數(shù)對象都有一個內(nèi)部的 [[scope]] 屬性,這個屬性也由對象列表(鏈)組成。這個內(nèi)部的[[scope]] 屬性引用的就是創(chuàng)建它們的執(zhí)行環(huán)境的作用域鏈,同時,當(dāng)前執(zhí)行環(huán)境的活動對象被添加到該對象列表的頂部。當(dāng)我們在函數(shù)內(nèi)部訪問變量時,其實(shí)就是在作用域鏈上尋找變量的過程。
理論性太強(qiáng)了(總結(jié)死我了!),還是讓我們來看一段代碼吧:
<script type="text/javascript">
function outer(){
var i = 10;
function inner(){
var j = 100;
alert(j);//100
alert(i);//10
alert(adf);
}
inner();
}
outer();
</script>
下圖清晰的展現(xiàn)了上述代碼的內(nèi)存分配與作用域分配情況:

下面來解釋一下:
1.載入代碼,創(chuàng)建全局執(zhí)行環(huán)境,此時會在可變對象(window)中添加outer變量,其指向于函數(shù)對象outer,此時作用域鏈中只有window對象.
2.執(zhí)行代碼,當(dāng)程序執(zhí)行到outer()時,會在全局對象中尋找outer變量,成功調(diào)用。
3.創(chuàng)建outer的執(zhí)行環(huán)境,此時會新創(chuàng)建一個活動對象,添加變量i,設(shè)置值為10,添加變量inner,指向于函數(shù)對象inner.并將活動對象壓入作用域鏈中.并將函數(shù)對象outer的[[scope]]屬性指向活動對象outer。此時作用域鏈為outer的活動對象+window.
4.執(zhí)行代碼,為 i 成功賦值。當(dāng)程序執(zhí)行到inner()時,會在函數(shù)對象outer的[[scope]]中尋找inner變量。找到后成功調(diào)用。
5.創(chuàng)建inner的執(zhí)行環(huán)境,新建一個活動對象,添加變量j,賦值為100,并將該活動對象壓入作用域鏈中,并函數(shù)對象inner的[[scope]]屬性指向活動對象inner.此時作用域鏈為:inner的活動對象+outer的活動對象+全局對象.
6.執(zhí)行代碼為j賦值,當(dāng)訪問i、j時成功在作用域中找到對應(yīng)的值并輸出,而當(dāng)訪問變量adf時,沒有在作用域中尋找到,訪問出錯。
注:通過內(nèi)存圖,我們會發(fā)現(xiàn)作用域鏈與prototype鏈?zhǔn)侨绱说南嘞蟆_@說明了很多問題...(仁者見仁智者見智,自己探尋答案吧!)
閉包原理
在我們了解了作用域的問題之后,對于閉包這個問題已經(jīng)很簡單了。什么是閉包?閉包就是封閉了外部函數(shù)作用域中變量的內(nèi)部函數(shù)。
我們來看一個典型的閉包運(yùn)用:生成increment值
<script type="text/javascript">
var increment = (function(){
var id = 0;
return function(){
return ++id;
}
})()
alert(increment());//1
alert(increment());//2
</script>
外層匿名函數(shù)返回的是一個內(nèi)嵌函數(shù),內(nèi)嵌函數(shù)使用了外層匿名函數(shù)的局部變量id。照理外層匿名函數(shù)的局部變量在返回時就超出了作用域因此increment()調(diào)用無法使用才對。這就是閉包Closure,即函數(shù)調(diào)用返回了一個內(nèi)嵌函數(shù),而內(nèi)嵌函數(shù)引用了外部函數(shù)的局部變量、參數(shù)等這些應(yīng)當(dāng)被關(guān)閉(Close)了的資源。這是怎么一回事呢?讓我們來尋找答案:

根據(jù)Scope Chain的理解可以解釋,返回的內(nèi)嵌函數(shù)已經(jīng)持有了構(gòu)造它時的Scope Chain,雖然outer返回導(dǎo)致這些對象超出了作用域、生存期范圍,但JavaScript使用自動垃圾回收來釋放對象內(nèi)存: 按照規(guī)則定期檢查,對象沒有任何引用才被釋放。因此上面的代碼能夠正確運(yùn)行。
參考:
http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html
http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html
在 ECMAScript 中,函數(shù)也是對象。函數(shù)對象在變量實(shí)例化過程中會根據(jù)函數(shù)聲明來創(chuàng)建,或者是在計(jì)算函數(shù)表達(dá)式或調(diào)用 Function 構(gòu)造函數(shù)時創(chuàng)建。(關(guān)于'函數(shù)對象'請見《理解Javascript_08_函數(shù)對象》)。每個函數(shù)對象都有一個內(nèi)部的 [[scope]] 屬性,這個屬性也由對象列表(鏈)組成。這個內(nèi)部的[[scope]] 屬性引用的就是創(chuàng)建它們的執(zhí)行環(huán)境的作用域鏈,同時,當(dāng)前執(zhí)行環(huán)境的活動對象被添加到該對象列表的頂部。當(dāng)我們在函數(shù)內(nèi)部訪問變量時,其實(shí)就是在作用域鏈上尋找變量的過程。
理論性太強(qiáng)了(總結(jié)死我了!),還是讓我們來看一段代碼吧:
復(fù)制代碼 代碼如下:
<script type="text/javascript">
function outer(){
var i = 10;
function inner(){
var j = 100;
alert(j);//100
alert(i);//10
alert(adf);
}
inner();
}
outer();
</script>
下圖清晰的展現(xiàn)了上述代碼的內(nèi)存分配與作用域分配情況:

下面來解釋一下:
1.載入代碼,創(chuàng)建全局執(zhí)行環(huán)境,此時會在可變對象(window)中添加outer變量,其指向于函數(shù)對象outer,此時作用域鏈中只有window對象.
2.執(zhí)行代碼,當(dāng)程序執(zhí)行到outer()時,會在全局對象中尋找outer變量,成功調(diào)用。
3.創(chuàng)建outer的執(zhí)行環(huán)境,此時會新創(chuàng)建一個活動對象,添加變量i,設(shè)置值為10,添加變量inner,指向于函數(shù)對象inner.并將活動對象壓入作用域鏈中.并將函數(shù)對象outer的[[scope]]屬性指向活動對象outer。此時作用域鏈為outer的活動對象+window.
4.執(zhí)行代碼,為 i 成功賦值。當(dāng)程序執(zhí)行到inner()時,會在函數(shù)對象outer的[[scope]]中尋找inner變量。找到后成功調(diào)用。
5.創(chuàng)建inner的執(zhí)行環(huán)境,新建一個活動對象,添加變量j,賦值為100,并將該活動對象壓入作用域鏈中,并函數(shù)對象inner的[[scope]]屬性指向活動對象inner.此時作用域鏈為:inner的活動對象+outer的活動對象+全局對象.
6.執(zhí)行代碼為j賦值,當(dāng)訪問i、j時成功在作用域中找到對應(yīng)的值并輸出,而當(dāng)訪問變量adf時,沒有在作用域中尋找到,訪問出錯。
注:通過內(nèi)存圖,我們會發(fā)現(xiàn)作用域鏈與prototype鏈?zhǔn)侨绱说南嘞蟆_@說明了很多問題...(仁者見仁智者見智,自己探尋答案吧!)
閉包原理
在我們了解了作用域的問題之后,對于閉包這個問題已經(jīng)很簡單了。什么是閉包?閉包就是封閉了外部函數(shù)作用域中變量的內(nèi)部函數(shù)。
我們來看一個典型的閉包運(yùn)用:生成increment值
復(fù)制代碼 代碼如下:
<script type="text/javascript">
var increment = (function(){
var id = 0;
return function(){
return ++id;
}
})()
alert(increment());//1
alert(increment());//2
</script>
外層匿名函數(shù)返回的是一個內(nèi)嵌函數(shù),內(nèi)嵌函數(shù)使用了外層匿名函數(shù)的局部變量id。照理外層匿名函數(shù)的局部變量在返回時就超出了作用域因此increment()調(diào)用無法使用才對。這就是閉包Closure,即函數(shù)調(diào)用返回了一個內(nèi)嵌函數(shù),而內(nèi)嵌函數(shù)引用了外部函數(shù)的局部變量、參數(shù)等這些應(yīng)當(dāng)被關(guān)閉(Close)了的資源。這是怎么一回事呢?讓我們來尋找答案:

根據(jù)Scope Chain的理解可以解釋,返回的內(nèi)嵌函數(shù)已經(jīng)持有了構(gòu)造它時的Scope Chain,雖然outer返回導(dǎo)致這些對象超出了作用域、生存期范圍,但JavaScript使用自動垃圾回收來釋放對象內(nèi)存: 按照規(guī)則定期檢查,對象沒有任何引用才被釋放。因此上面的代碼能夠正確運(yùn)行。
參考:
http://www.cnblogs.com/RicCC/archive/2008/02/15/JavaScript-Object-Model-Execution-Model.html
http://www.cn-cuckoo.com/2007/08/01/understand-javascript-closures-72.html
相關(guān)文章
JavaScript實(shí)現(xiàn)隨機(jī)點(diǎn)名的示例代碼
這篇文章主要為大家詳細(xì)介紹了如何使用JavaScript實(shí)現(xiàn)隨機(jī)點(diǎn)名效果,文中的示例代碼簡潔易懂,具有一定的借鑒價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11JS對象與json字符串格式轉(zhuǎn)換實(shí)例
這篇文章主要介紹了JS對象與json字符串格式轉(zhuǎn)換方法,以實(shí)例的形式詳細(xì)講述了js對象與json字符串格式轉(zhuǎn)換的技巧,需要的朋友可以參考下2014-10-10p5.js實(shí)現(xiàn)聲音控制警察抓小偷游戲示例解析
這篇文章主要為大家介紹了p5.js實(shí)現(xiàn)聲音控制警察抓小偷游戲示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04JavaScript encodeURI 和encodeURIComponent
encodeURI和encodeURIComponet函數(shù)都是javascript中用來對URI進(jìn)行編碼,將相關(guān)參數(shù)轉(zhuǎn)換成UTF-8編碼格式的數(shù)據(jù)。URI在進(jìn)行定位跳轉(zhuǎn)時,參數(shù)里面的中文、日文等非ASCII編碼都會進(jìn)行編碼轉(zhuǎn)換2015-12-12javascript文本框內(nèi)輸入文字倒計(jì)數(shù)的方法
這篇文章主要介紹了javascript文本框內(nèi)輸入文字倒計(jì)數(shù)的方法,涉及javascript針對鍵盤事件的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-02-02最全的JavaScript開發(fā)工具列表 總有一款適合你
最全的JavaScript開發(fā)工具列表分享給你,總有一款適合你!2017-06-06