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

JavaScript中的作用域鏈和閉包

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

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

作用域(scope)

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

全局作用域(Global Scope)

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

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

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

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

blogName="CSDN李達(dá)"

3、所有window對象的屬性擁有全局作用域,例如:window對象的內(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); //錯誤
inFunction(); //錯誤</span>

作用域鏈(scope chain)

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

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

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

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

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

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

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

var total = add(5,10);

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

活動對象(Active Object)

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

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

<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);//給出點擊反應(yīng)
}
}
}
window.onload = newLoad; //把newload事件賦值給頁面加載
</script>

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

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

運行結(jié)果:無論點擊那個anchor,總會彈出anchor4,而我們根本就沒有anchor4:

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

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

閉包優(yōu)化

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

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

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

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

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

因為接觸javascript時間尚短,理解有誤的地方歡迎指正。

相關(guān)文章

最新評論