欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript中的作用域鏈和閉包

 更新時(shí)間:2012年06月30日 00:36:32   作者:  
JavaScript中出現(xiàn)了一個(gè)以前沒(méi)學(xué)過(guò)的概念——閉包。何為閉包?從表面理解即封閉的包,與作用域有關(guān)。所以,說(shuō)閉包以前先說(shuō)說(shuō)作用域
作用域
全局作用域
局部作用域
作用域鏈
執(zhí)行上下文
活動(dòng)對(duì)象
閉包
閉包優(yōu)化

JavaScript中出現(xiàn)了一個(gè)以前沒(méi)學(xué)過(guò)的概念——閉包。何為閉包?從表面理解即封閉的包,與作用域有關(guān)。所以,說(shuō)閉包以前先說(shuō)說(shuō)作用域。

作用域(scope)

通常來(lái)說(shuō)一段程序代碼中使用的變量和函數(shù)并不總是可用的,限定其可用性的范圍即作用域,作用域的使用提高了程序邏輯的局部性,增強(qiáng)程序的可靠性,減少名字沖突。

全局作用域(Global Scope)

在代碼中任何地方都能訪問(wèn)到的對(duì)象擁有全局作用域,以下幾種情形擁有全局作用域:

1、最外層函數(shù)和在最外層函數(shù)外面定義的變量擁有全局作用域,例如:
復(fù)制代碼 代碼如下:

var outSide="var outside";
function outFunction(){
var name="var inside";
function inSideFunction(){
alert(name);
}
inSideFunction();
}
alert(outSide); //正確
alert(name); //錯(cuò)誤
outFunction(); //正確
inSideFunction() //錯(cuò)誤

2、未定義直接賦值的變量自動(dòng)聲明為擁有全局作用域,例如:
復(fù)制代碼 代碼如下:

blogName="CSDN李達(dá)"

3、所有window對(duì)象的屬性擁有全局作用域,例如:window對(duì)象的內(nèi)置屬性都擁有全局作用域,例如window.name、window.location、window.top等

局部作用域(Local Scope)
復(fù)制代碼 代碼如下:

<span style="font-family: SimSun; ">function outFunction(){
var name="inside name";
function inFunction(){
alert(name);
}
inFunction();
}
alert(name); //錯(cuò)誤
inFunction(); //錯(cuò)誤</span>

作用域鏈(scope chain)

JavaScript中,JavaScript里一切都是對(duì)象,包括函數(shù)。函數(shù)對(duì)象和其它對(duì)象一樣,擁有可以通過(guò)代碼訪問(wèn)的屬性和一系列僅供JavaScript引擎訪問(wèn)的內(nèi)部屬性。其中一個(gè)內(nèi)部屬性是作用域,包含了函數(shù)被創(chuàng)建的作用域中對(duì)象的集合,稱(chēng)為函數(shù)的作用域鏈,它決定了哪些數(shù)據(jù)能被函數(shù)訪問(wèn)。
  當(dāng)一個(gè)函數(shù)創(chuàng)建后,它的作用域鏈會(huì)被創(chuàng)建此函數(shù)的作用域中可訪問(wèn)的數(shù)據(jù)對(duì)象填充。例如函數(shù):
復(fù)制代碼 代碼如下:

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

在函數(shù)add創(chuàng)建時(shí),它的作用域鏈中會(huì)填入一個(gè)全局對(duì)象,該全局對(duì)象包含了所有全局變量,如下圖所示(注意:圖片只例舉了全部變量中的一部分):

 
由此可見(jiàn),函數(shù)的作用域鏈?zhǔn)莿?chuàng)建函數(shù)的時(shí)候創(chuàng)建的。

執(zhí)行上下文(Execute context )

函數(shù)add的作用域?qū)?huì)在執(zhí)行時(shí)用到,例如:

復(fù)制代碼 代碼如下:

var total = add(5,10);

當(dāng)執(zhí)行 add 函數(shù)的時(shí)候, JavaScript 會(huì)創(chuàng)建一個(gè) Execute context (執(zhí)行上下文),執(zhí)行上下文中就包含了 add 函數(shù)運(yùn)行期所需要的所有信息。 Execute context 也有自己的 Scope chain, 當(dāng)函數(shù)運(yùn)行時(shí), JavaScript 引擎會(huì)首先從用 add 函數(shù)的作用域鏈來(lái)初始化執(zhí)行上下文的作用域鏈。

活動(dòng)對(duì)象(Active Object)

然后 JavaScript 引擎又會(huì)創(chuàng)建一個(gè) Active Object, 這些值按照它們出現(xiàn)在函數(shù)中的順序被復(fù)制到運(yùn)行期上下文的作用域鏈中,它們共同組成了一個(gè)新的對(duì)象——“活動(dòng)對(duì)象(activation object)”,這個(gè)對(duì)象里面包含了函數(shù)運(yùn)行期的所有局部變量,參數(shù)以及 this 等變量,此對(duì)象會(huì)被推入作用域鏈的前端,當(dāng)運(yùn)行期上下文被銷(xiāo)毀,活動(dòng)對(duì)象也隨之銷(xiāo)毀。新的作用域鏈如下圖所示:

執(zhí)行上下文是一個(gè)動(dòng)態(tài)的概念,當(dāng)函數(shù)運(yùn)行的時(shí)候創(chuàng)建,活動(dòng)對(duì)象 Active Object 也是一個(gè)動(dòng)態(tài)的概念,它是被執(zhí)行上下文的作用域鏈引用的,可以得出結(jié)論:執(zhí)行上下文和活動(dòng)對(duì)象都是動(dòng)態(tài)概念,并且執(zhí)行上下文的作用域鏈?zhǔn)怯珊瘮?shù)作用域鏈初始化的。
在函數(shù)執(zhí)行過(guò)程中,每遇到一個(gè)變量,都會(huì)檢索從哪里獲取和存儲(chǔ)數(shù)據(jù),該過(guò)程從作用域鏈頭部,也就是從活動(dòng)對(duì)象開(kāi)始搜索,查找同名的標(biāo)識(shí)符,如果找到了就使用這個(gè)標(biāo)識(shí)符對(duì)應(yīng)的變量,如果沒(méi)有則繼續(xù)搜索作用域鏈中的下一個(gè)對(duì)象,如果搜索完所有對(duì)象都未找到,則認(rèn)為該標(biāo)識(shí)符未定義,函數(shù)執(zhí)行過(guò)程中,每個(gè)標(biāo)識(shí)符都要經(jīng)歷這樣的搜索過(guò)程。
閉包(closure)
先來(lái)看一個(gè)實(shí)例,javascript代碼:
復(fù)制代碼 代碼如下:

<script type="text/javascript">
function newLoad(){ //新建頁(yè)面加載的事件
for (var i = 1; i <=3; i++) {
var anchor = document.getElementById("anchor" + i); //找到每個(gè)anchor
anchor.onclick = function () {//為anchor添加單擊事件
alert ("you clicked anchor"+i);//給出點(diǎn)擊反應(yīng)
}
}
}
window.onload = newLoad; //把newload事件賦值給頁(yè)面加載
</script>

前臺(tái)代碼:
復(fù)制代碼 代碼如下:

<body>
<a id="anchor1" href="#">anchor1</a><br/>
<a id="anchor2" href="#">anchor2</a><br/>
<a id="anchor3" href="#">anchor3</a><br/>
</body>

運(yùn)行結(jié)果:無(wú)論點(diǎn)擊那個(gè)anchor,總會(huì)彈出anchor4,而我們根本就沒(méi)有anchor4:

當(dāng)我們加載頁(yè)面時(shí),javascript中的newLoad函數(shù)已經(jīng)運(yùn)行完畢,由其中的循環(huán)可知,anchor已經(jīng)賦值為4。但是由以前的編程經(jīng)驗(yàn)來(lái)看,局部變量使用完畢就會(huì)銷(xiāo)毀,但是anchor卻沒(méi)有,顯然這里 JavaScript 采用了另外的方式。

閉包在 JavaScript 其實(shí)就是一個(gè)函數(shù),在函數(shù)運(yùn)行期被創(chuàng)建的,當(dāng)上面的 函數(shù)被執(zhí)行的時(shí)候,會(huì)創(chuàng)建一個(gè)閉包,而這個(gè)閉包會(huì)引用newLoad 作用域中的anchor。下面就來(lái)看看 JavaScript 是如何來(lái)實(shí)現(xiàn)閉包的:當(dāng)執(zhí)行 newLoad 函數(shù)的時(shí)候, JavaScript 引擎會(huì)創(chuàng)建newLoad函數(shù)執(zhí)行上下文的作用域鏈,這個(gè)作用域鏈包含了 newLoad執(zhí)行時(shí)的活動(dòng)對(duì)象,同時(shí) JavaScript 引擎也會(huì)創(chuàng)建一個(gè)閉包,而閉包的作用域鏈也會(huì)引用 newload的活動(dòng)對(duì)象,這樣當(dāng) newload執(zhí)行完的時(shí)候,雖然其執(zhí)行上下文和活動(dòng)對(duì)象都已經(jīng)釋放了anchor,但是閉包還是引用著 newload 的活動(dòng)對(duì)象,所以點(diǎn)擊顯示的是“you clicked anchor4”。運(yùn)行期如圖:

閉包優(yōu)化

既然閉包出現(xiàn)了我們不想看到的結(jié)果,我們需要優(yōu)化它。優(yōu)化后的javascript(其他不變):

復(fù)制代碼 代碼如下:

<script type="text/javascript">
function newLoad() { //新建頁(yè)面加載的事件
for (var i = 1; i <= 3; i++) {
var anchor = document.getElementById("anchor" + i); //找到每個(gè)anchor
listener(anchor,i);//listener函數(shù)
}
}
function listener(anchor, i) {
anchor.onclick = function () {//為anchor添加單擊事件
alert("you clicked anchor" + i); //給出點(diǎn)擊反應(yīng)
}
}
window.onload = newLoad; //把newload事件賦值給頁(yè)面加載
</script>

運(yùn)行結(jié)果:提示對(duì)應(yīng)的提示信息

結(jié)果分析:優(yōu)化后的結(jié)果是因?yàn)?,我們把聲明的變量和單擊事件相分離。用以上解釋的作用域鏈解釋?zhuān)喉?yè)面加載,先執(zhí)行l(wèi)istener函數(shù),而listener函數(shù)需要anchor變量,在listener函數(shù)作用域鏈中沒(méi)有,會(huì)進(jìn)入下一級(jí)作用域鏈,即查找newLoad中的anchor,因?yàn)樵趌istener中已經(jīng)確定了哪個(gè)anchor單擊對(duì)應(yīng)哪個(gè)提示信息,所以只是在newload中查找對(duì)應(yīng)的anchor而已,不會(huì)等循環(huán)完畢產(chǎn)生anchor4。

因?yàn)榻佑|javascript時(shí)間尚短,理解有誤的地方歡迎指正。

相關(guān)文章

最新評(píng)論