Javascript中閉包的作用域鏈
作用域定義了在當(dāng)前上下文中能夠被訪問到的成員,在Javascript中分為全局作用域和函數(shù)作用域,通過函數(shù)嵌套可以實(shí)現(xiàn)嵌套作用域。
閉包一般發(fā)生在嵌套作用域中。閉包是JavaScript最強(qiáng)大的特性之一,很多高級應(yīng)用都要依靠閉包實(shí)現(xiàn)。如OO的私有成員和模塊化等。然而閉包雖然強(qiáng)大,但一般比較占用內(nèi)存另外如果使用不當(dāng)還會引起內(nèi)存泄露,對閉包有了解的jser一般都知道閉包的這些個問題,本文先闡述了閉包和作用域鏈的關(guān)系,然后分析了產(chǎn)生這些個問題的原因。下面是一段閉包的示例代碼:
- var name = "windows's name";
 - var object = {
 - name : "object's name",
 - getNameFunc:function(){
 - var that = this;
 - return function(){
 - return that.name;
 - }
 - }
 - };
 - console.log(object.getNameFunc()());
 
當(dāng)javascript代碼執(zhí)行時會創(chuàng)建一個執(zhí)行上下文對象,執(zhí)行上下文對象包含一個作用域鏈,作用域鏈有一個或多個變量對象組成,作用域鏈中保存的是對變量對象的引用。變量對象定義了在當(dāng)前作用域中聲明的變量和函數(shù)。
在代碼的執(zhí)行過程中JAVASCRIPT引擎會按照自上而下的順序檢索作用鏈中的變量對象的成員來解析需要被訪問(讀或?qū)?的變量或函數(shù),在檢索過程中如果在作用域鏈的某個變量對中象查找到與之匹配的標(biāo)識符就會終止檢索。
通過這種方式當(dāng)在代碼中如果有同名的變量就可以區(qū)分出來我們要操作那個變量。當(dāng)代碼載入完成后 Javascript引擎會創(chuàng)建一個全局的執(zhí)行上下文,全局執(zhí)行上下文的作用域鏈只包含一個全局變量對象。Javascript的函數(shù)對象擁有一個私有的socpe屬性,當(dāng)函數(shù)被創(chuàng)建時會使用當(dāng)前的作用域鏈初始化函數(shù)scope屬性。
當(dāng)函數(shù)被執(zhí)行時會創(chuàng)建函數(shù)的執(zhí)行上下文對象和當(dāng)前作用域的變量對象,創(chuàng)建函數(shù)的執(zhí)行上下文對象時先使用函數(shù)的scope屬性給執(zhí)行上下文對象的作用域鏈賦值,再把創(chuàng)建的變量對象放入作用域鏈的頂端來初始化執(zhí)行上下文的作用域鏈。位于作用域鏈頂端的變量對象也稱為活動對象。默認(rèn)情況下當(dāng)函數(shù)返回時會銷毀它的活動對象和作用域鏈,這也是為什么我們函數(shù)的外部不能夠訪問到在函數(shù)內(nèi)部定義的成員。
然而在發(fā)生閉包的情況下,當(dāng)內(nèi)部函數(shù)被創(chuàng)建時,內(nèi)部函數(shù)的scope屬性指向包含它的外部函數(shù)的作用域鏈。這時內(nèi)部函數(shù)引用了外部函數(shù)的活動對象,當(dāng)外部函數(shù)返回時,它的活動對象沒有被釋放,直到引用它的內(nèi)部函數(shù)被銷毀時才會釋放外層函數(shù)的活動對象。所以它所包含的內(nèi)部函數(shù)仍然可以訪問在外部函數(shù)中定義的成員,另外這也是閉包比較占用內(nèi)存的原因。
內(nèi)存泄露問題是由于部分瀏覽器使用引用計數(shù)來作為垃圾回收機(jī)制,當(dāng)在我們的代碼中發(fā)生了循環(huán)引用時,就會造成資源不能被回收從而引起內(nèi)存泄露。發(fā)生內(nèi)存泄露有時是因?yàn)樵谖覀兊拇a中發(fā)生了明顯的循環(huán)引用,有時則不那么明顯。對于前者一般通過仔細(xì)檢查代碼就能發(fā)現(xiàn)問題所在,對于后者就需要我們對閉包有足夠的了解才能發(fā)現(xiàn)。如下面代碼:
- view sourceprint?function outerFunc(){
 - var obj = document.getElementById("element");
 - obj.onclick=function innerFunc(){
 - alert("Hi! I will leak");
 - };
 - obj.bigString=new Array(1000).join(new Array(3000).join("XXXXX"));
 - // This is used to make the leak significant
 - };
 
在上面這段代碼中,外部函數(shù)outerFunc中的局部變量obj引用文檔中ID為element的Dom元素,obj.onclick指向內(nèi)部函數(shù)innerFunc的引用,內(nèi)部函數(shù)innerFunc的scope屬性指向的作用鏈中包含外部函數(shù)outerFunc的活動對象的引用,活動對象的obj成員再次指向文檔中ID為element的Dom元素從而構(gòu)成了循環(huán)引用。
【編輯推薦】















 
 
 






 
 
 
 