成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

溫故js系列(14)-閉包&垃圾回收&內(nèi)存泄露&閉包應(yīng)用&作用域鏈&

Amio / 1571人閱讀

摘要:該對(duì)象包含了函數(shù)的所有局部變量命名參數(shù)參數(shù)集合以及,然后此對(duì)象會(huì)被推入作用域鏈的前端。如果整個(gè)作用域鏈上都無(wú)法找到,則返回。此時(shí)的作用域鏈包含了兩個(gè)對(duì)象的活動(dòng)對(duì)象和對(duì)象。

前端學(xué)習(xí):教程&開發(fā)模塊化/規(guī)范化/工程化/優(yōu)化&工具/調(diào)試&值得關(guān)注的博客/Git&面試-前端資源匯總

歡迎提issues斧正:閉包

JavaScript-閉包

閉包(closure)是一個(gè)讓人又愛又恨的something,它可以實(shí)現(xiàn)很多高級(jí)功能和應(yīng)用,同時(shí)在理解和應(yīng)用上有很多難點(diǎn)和需要小心注意的地方。

閉包的定義

閉包,官方對(duì)閉包的解釋是:一個(gè)擁有許多變量和綁定了這些變量的環(huán)境的表達(dá)式(通常是一個(gè)函數(shù)),因而這些變量也是該表達(dá)式的一部分。
簡(jiǎn)單來(lái)說(shuō),閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。在Javascript中,只有函數(shù)內(nèi)部的子函數(shù)才能讀取函數(shù)的局部變量,所以,可以把閉包理解成:定義在一個(gè)函數(shù)內(nèi)部的函數(shù),也就是函數(shù)嵌套函數(shù),給函數(shù)內(nèi)部和函數(shù)外部搭建起一座橋梁。

閉包的特點(diǎn)

定義在一個(gè)函數(shù)內(nèi)部的函數(shù)。

函數(shù)內(nèi)部可以引用函數(shù)外部的參數(shù)和變量。

作為一個(gè)函數(shù)變量的一個(gè)引用,當(dāng)函數(shù)返回時(shí),其處于激活狀態(tài)。

當(dāng)一個(gè)函數(shù)返回時(shí),一個(gè)閉包就是一個(gè)沒(méi)有釋放資源的棧區(qū)。函數(shù)的參數(shù)和變量不會(huì)被垃圾回收機(jī)制回收。

閉包的形成

Javascript允許使用內(nèi)部函數(shù),可以將函數(shù)定義和函數(shù)表達(dá)式放在另一個(gè)函數(shù)的函數(shù)體內(nèi)。而且,內(nèi)部函數(shù)可以訪問(wèn)它所在的外部函數(shù)聲明的局部變量、參數(shù)以及聲明的其他內(nèi)部函數(shù)。當(dāng)其中一個(gè)這樣的內(nèi)部函數(shù)在包含它們的外部函數(shù)之外被調(diào)用時(shí),就會(huì)形成閉包。

function a() {  
    var i = 0;  
    function b() { 
        console.log(i++); 
    }  
    return b; 
}
var c = a(); 
c();
閉包的缺點(diǎn)

1.由于閉包會(huì)使得函數(shù)中的變量都被保存在內(nèi)存中,內(nèi)存消耗很大。所以在閉包不用之后,將不使用的局部變量刪除,使其被回收。在IE中可能導(dǎo)致內(nèi)存泄露,即無(wú)法回收駐留在內(nèi)存中的元素,這時(shí)候需要手動(dòng)釋放。

function a() {  
    var i = 1;  
    function b() { 
        console.log(i++); 
    }  
    return b; 
}
var c = a(); 
c(); //1
c(); //2
c(); //3   i不被回收
c = null;  //i被回收

2.閉包會(huì)在父函數(shù)外部,改變父函數(shù)內(nèi)部變量的值。如果你把父函數(shù)當(dāng)作對(duì)象使用,把閉包當(dāng)作它的公用方法,把內(nèi)部變量當(dāng)作它的私有屬性,要小心,不要隨便改變父函數(shù)內(nèi)部變量的值。

var Xzavier = { 
    ten:10,  
    addTen: function(num) {  
       return this.ten + num;   //給一個(gè)數(shù)加10 
   }    
}
 
console.log(Xzavier.addTen(15));  //25
Xzavier.ten = 20; 
console.log(Xzavier.addTen(15));  //35
內(nèi)存泄露

內(nèi)存泄漏指一塊被分配的內(nèi)存既不能使用,又不能回收,直到瀏覽器進(jìn)程結(jié)束。

出現(xiàn)原因:

1.循環(huán)引用:含有DOM對(duì)象的循環(huán)引用將導(dǎo)致大部分當(dāng)前主流瀏覽器內(nèi)存泄露。循環(huán) 引用,簡(jiǎn)單來(lái)說(shuō)假如a引用了b,b又引用了a,a和b就構(gòu)成了循環(huán)引用。
2.JS閉包:閉包,函數(shù)返回了內(nèi)部函數(shù)還可以繼續(xù)訪問(wèn)外部方法中定義的私有變量。
3.Dom泄露,當(dāng)原有的DOM被移除時(shí),子結(jié)點(diǎn)引用沒(méi)有被移除則無(wú)法回收。  
JavaScript垃圾回收機(jī)制

Javascript中,如果一個(gè)對(duì)象不再被引用,那么這個(gè)對(duì)象就會(huì)被GC(garbage collection)回收。如果兩個(gè)對(duì)象互相引用,而不再被第3者所引用,那么這兩個(gè)互相引用的對(duì)象也會(huì)被回收。垃圾回收不是時(shí)時(shí)的,因?yàn)槠溟_銷比較大,所以垃圾回收器會(huì)按照固定的時(shí)間間隔周期性的執(zhí)行。

函數(shù)a被b引用,b又被a外的c引用,這就是為什么函數(shù)a執(zhí)行后不會(huì)被回收的原因。

垃圾回收的兩個(gè)方法:

標(biāo)記清除法:

1.垃圾回收機(jī)制給存儲(chǔ)在內(nèi)存中的所有變量加上標(biāo)記,然后去掉環(huán)境中的變量以及被環(huán)境中變量所引用的變量(閉包)。
2.操作1之后內(nèi)存中仍存在標(biāo)記的變量就是要?jiǎng)h除的變量,垃圾回收機(jī)制將這些帶有標(biāo)記的變量回收。

引用計(jì)數(shù)法:

1.垃圾回收機(jī)制給一個(gè)變量一個(gè)引用次數(shù),當(dāng)聲明了一個(gè)變量并將一個(gè)引用類型賦值給該變量的時(shí)候這個(gè)值的引用次數(shù)就加1。
2.當(dāng)該變量的值變成了另外一個(gè)值,則這個(gè)值得引用次數(shù)減1。
3.當(dāng)這個(gè)值的引用次數(shù)變?yōu)?的時(shí)候,說(shuō)明沒(méi)有變量在使用,垃圾回收機(jī)制會(huì)在運(yùn)行的時(shí)候清理掉引用次數(shù)為0的值占用的空間。
閉包的應(yīng)用 1.維護(hù)函數(shù)內(nèi)的變量安全,避免全局變量的污染。

函數(shù)a中i只有函數(shù)b才能訪問(wèn),而無(wú)法通過(guò)其他途徑訪問(wèn)到。

function xzavier(){
    var i = 1;
    i++;
    console.log(i);
}
xzavier();   //2 
console.log(x);   // x is not defined                 
xzavier();   //2
2.維持一個(gè)變量不被回收。

由于閉包,函數(shù)a中i的一直存在于內(nèi)存中,因此每次執(zhí)行c(),都會(huì)給i自加1,且i不被垃圾回收機(jī)制回收。

function a() {  
    var i = 1;  
    function b() { 
        console.log(i++); 
    }  
    return b; 
}
var c = a(); 
c();  //1
c();  //2
c();  //3
3.通過(guò)第1點(diǎn)的特性設(shè)計(jì)私有的方法和屬性。
var xzavier = (function(){
    var i = 1;
    var s = "xzavier";
    function f(){
        i++;
        console.log(i);
    }
    return {
        i:i,
        s:s,             
        f:f
    }
})();
xzavier.s;     //"xzavier"
xzavier.s;     //1
xzavier.f()    //2
4.操作DOM獲取目標(biāo)元素

方法2即使用了閉包的方法,當(dāng)然操作DOM還是有別的方法的,比如事件委托就比較好用。

ul id="test">
    
  • first
  • second
  • third
  • // 方法一:this方法 var lis = document.getElementById("test").getElementsByTagName("li"); for(var i = 0;i < 3;i++){ lis[i].index = i; lis[i].onclick = function(){ console.log(this.index); }; } // 方法二:閉包方法 var lis = document.getElementById("test").getElementsByTagName("li"); for(var i = 0;i < 3;i++){ lis[i].index = i; lis[i].onclick = (function(val){ return function() { console.log(val); } })(i); } // 方法三 事件委托方法 var oUl = document.getElementById("test"); oUl.addEventListener("click",function(e){ var lis = e.target; console.log(lis); });
    5.封裝模塊

    邏輯隨業(yè)務(wù)復(fù)雜而復(fù)雜O(∩_∩)O~

    var Xzavier = function(){       
        var name = "xzavier";       
        return {    
           getName : function(){    
               return name;    
           },    
           setName : function(newName){    
               name = newName;    
           }    
        }    
    }();    
    
    console.log(person.name); //undefined,變量作用域?yàn)楹瘮?shù)內(nèi)部,外部無(wú)法訪問(wèn)    
    console.log(person.getName()); // "xzavier" 
    person.setName("xz");    
    console.log(person.getName());  //"xz"
    6.實(shí)現(xiàn)類和繼承
    function Xzavier(){       
        var name = "xzavier";       
        return {    
           getName : function(){    
               return name;    
           },    
           setName : function(newName){    
               name = newName;    
           }    
        }    
    }
    
    var xz = new Xzavier();  //Xzavier就是一個(gè)類,可以實(shí)例化
    console.log(xz.getName());  // "xzavier"  

    這里是原型繼承,我會(huì)在下一篇文章講一講原型繼承。

    var X = function(){};
    X.prototype = new Xzavier(); 
    X.prototype.sports = function(){
        console.log("basketball");
    };
    var x = new X();
    x.setName("xz");
    x.sports();  //"basketball"
    console.log(x.getName());  //"xz"
    
    JavaScript作用域鏈

    JavaScript作用域

    作用域就是變量與函數(shù)的可訪問(wèn)范圍,即作用域控制著變量與函數(shù)的可見性和生命周期。
    在JavaScript中,變量的作用域有全局作用域和局部作用域兩種。
    

    JavaScript作用域鏈

    JavaScript函數(shù)對(duì)象擁有可以通過(guò)代碼訪問(wèn)的屬性和一系列僅供JavaScript引擎訪問(wèn)的內(nèi)部屬性。
    其中一個(gè)內(nèi)部屬性是[[Scope]],該內(nèi)部屬性包含了函數(shù)被創(chuàng)建的作用域中對(duì)象的集合。
    這個(gè)集合被稱為函數(shù)的作用域鏈。

    執(zhí)行上下文

    當(dāng)函數(shù)執(zhí)行時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行上下文(execution context),執(zhí)行上下文是一個(gè)內(nèi)部對(duì)象,定義了函數(shù)執(zhí)行時(shí)的環(huán)境。
    每個(gè)執(zhí)行上下文都有自己的作用域鏈,用于標(biāo)識(shí)符解析。
    當(dāng)執(zhí)行上下文被創(chuàng)建時(shí),而它的作用域鏈初始化為當(dāng)前運(yùn)行函數(shù)的[[Scope]]包含的對(duì)象。

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

    這些值按照它們出現(xiàn)在函數(shù)中的順序被復(fù)制到執(zhí)行上下文的作用域鏈中。
    它們共同組成了一個(gè)新的對(duì)象,活動(dòng)對(duì)象(activation object)。
    該對(duì)象包含了函數(shù)的所有局部變量、命名參數(shù)、參數(shù)集合以及this,然后此對(duì)象會(huì)被推入作用域鏈的前端。
    當(dāng)執(zhí)行上下文被銷毀,活動(dòng)對(duì)象也隨之銷毀。
    活動(dòng)對(duì)象是一個(gè)擁有屬性的對(duì)象,但它不具有原型而且不能通過(guò)JavaScript代碼直接訪問(wèn)。
    

    查找機(jī)制:

    1.當(dāng)函數(shù)訪問(wèn)一個(gè)變量時(shí),先搜索自身的活動(dòng)對(duì)象,如果存在則返回,如果不存在將繼續(xù)搜索函數(shù)父函數(shù)的活動(dòng)對(duì)象,依次查找,直到找到為止。
    2.如果函數(shù)存在prototype原型對(duì)象,則在查找完自身的活動(dòng)對(duì)象后先查找自身的原型對(duì)象,再繼續(xù)查找。
    3.如果整個(gè)作用域鏈上都無(wú)法找到,則返回undefined。
    

    在執(zhí)行上下文的作用域鏈中,標(biāo)識(shí)符所在的位置越深,讀寫速度就會(huì)越慢。全局變量總是存在于執(zhí)行上下文作用域鏈的最末端,因此在標(biāo)識(shí)符解析的時(shí)候,查找全局變量是最慢的。

    so

    在編寫代碼的時(shí)候應(yīng)盡量少使用全局變量,盡可能使用局部變量。
    我們經(jīng)常使用局部變量先保存一個(gè)多次使用的需要跨作用取的值再使用。
    
    再析閉包
    function a() {  
        var i = 1;  
        function b() { 
            console.log(i++); 
        }  
        return b; 
    }
    var c = a(); 
    c();
    
    1.當(dāng)定義函數(shù)a,js解釋器將函數(shù)a的作用域鏈設(shè)置為定義a時(shí)a所在的環(huán)境。
    2.執(zhí)行函數(shù)a的時(shí)候,a會(huì)進(jìn)入相應(yīng)的執(zhí)行上下文。
    3.在創(chuàng)建執(zhí)行上下文的過(guò)程中,首先會(huì)為a添加一個(gè)scope屬性,即a的作用域,其值就為a的作用域鏈。
    4.然后執(zhí)行上下文會(huì)創(chuàng)建一個(gè)活動(dòng)對(duì)象。
    5.創(chuàng)建完活動(dòng)對(duì)象后,把活動(dòng)對(duì)象添加到a的作用域鏈的最頂端。此時(shí)a的作用域鏈包含了兩個(gè)對(duì)象:a的活動(dòng)對(duì)象和window對(duì)象。
    6.接著在活動(dòng)對(duì)象上添加一個(gè)arguments屬性,它保存著調(diào)用函數(shù)a時(shí)所傳遞的參數(shù)。
    7.最后把所有函數(shù)a的形參和內(nèi)部的函數(shù)b的引用也添加到a的活動(dòng)對(duì)象上。    
      在這一步中,完成了函數(shù)b的的定義(如同a),函數(shù)b的作用域鏈被設(shè)置為b所被定義的環(huán)境,即a的作用域。
    8.整個(gè)函數(shù)a從定義到執(zhí)行的步驟完成。

    a返回函數(shù)b的引用給c,因?yàn)楹瘮?shù)b的作用域鏈包含了對(duì)函數(shù)a的活動(dòng)對(duì)象的引用,也就是說(shuō)b可以訪問(wèn)到a中定義的所有變量和函數(shù)。函數(shù)b被c引用,函數(shù)b又依賴函數(shù)a,因此函數(shù)a在返回后不會(huì)被GC回收,所以形成了閉包。

    文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

    轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/90921.html

    相關(guān)文章

    • 詳解Javascript的作用域、作用域鏈以及閉包

        一、我們先說(shuō)說(shuō)javascript的作用域 ?、偃肿兞?函數(shù)體外部進(jìn)行聲明 ?、诰植孔兞?函數(shù)體內(nèi)部進(jìn)行聲明  1)函數(shù)級(jí)作用域  javascript語(yǔ)言中局部變量不同于C#、Java等高級(jí)語(yǔ)言,在這些高級(jí)語(yǔ)言內(nèi)部,采用的塊級(jí)作用域中會(huì)聲明新的變量,這些變量不會(huì)影響到外部作用域。  而javascript則采用的是函數(shù)級(jí)作用域,也就是說(shuō)js創(chuàng)建作用域的單位是函數(shù)。  例如:  在C#當(dāng)中我...

      3403771864 評(píng)論0 收藏0
    • JavaScript之內(nèi)存回收&amp;&amp;內(nèi)存泄漏

      摘要:內(nèi)存回收內(nèi)存泄漏前言最近在細(xì)讀高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書中很多地方一筆帶過(guò),所以用自己所理解的,嘗試細(xì)致解讀下。內(nèi)存回收在談內(nèi)存泄漏之前,首先,先了解下的內(nèi)存回收機(jī)制。 內(nèi)存回收 && 內(nèi)存泄漏 前言:最近在細(xì)讀Javascript高級(jí)程序設(shè)計(jì),對(duì)于我而言,中文版,書中很多地方一筆帶過(guò),所以用自己所理解的,嘗試細(xì)致解讀下。如有紕漏或錯(cuò)誤,會(huì)非常感謝您的指出。文中絕大部分內(nèi)容...

      dayday_up 評(píng)論0 收藏0
    • 詳細(xì)解說(shuō)JavaScript內(nèi)存管理和GC算法

        JavaScript在創(chuàng)建變量(數(shù)組、字符串、對(duì)象等)是自動(dòng)進(jìn)行了分配內(nèi)存,而且當(dāng)它沒(méi)有被使用的狀態(tài)下,會(huì)自動(dòng)的釋放分配的內(nèi)容;其實(shí)這樣基層語(yǔ)言,如C語(yǔ)言,他們提供了內(nèi)存管理的接口,比如malloc()用于分配所需的內(nèi)存空間、free()釋放之前所分配的內(nèi)存空間?! ♂尫艃?nèi)存的過(guò)程稱為垃圾回收,例如avaScript這類高級(jí)語(yǔ)言可以提供了內(nèi)存自動(dòng)分配和自動(dòng)回收,其實(shí)這個(gè)自動(dòng)儲(chǔ)存不會(huì)占用太多空間...

      3403771864 評(píng)論0 收藏0
    • 10個(gè)優(yōu)化JavaScript代碼實(shí)用小tips

        說(shuō)道JavaScript的代碼優(yōu)化,就先要做的是準(zhǔn)確的測(cè)試JavaScript的代碼執(zhí)行時(shí)間。簡(jiǎn)單來(lái)說(shuō)就是采集大量的執(zhí)行樣本進(jìn)行數(shù)學(xué)統(tǒng)計(jì)和分析,這里我們使用的是benchmark.js來(lái)檢測(cè)代碼的執(zhí)行情況?! ∈紫任覀冃枰陧?xiàng)目中安裝依賴,代碼如下:  yarnaddbenchmark--save   #或者   npmibenchmark--save  然后我們寫一個(gè)測(cè)試代碼,如下所示:  ...

      3403771864 評(píng)論0 收藏0
    • 【前端芝士樹】Js中的閉包是怎么一回事 &amp;&amp; 筆試問(wèn)題集錦

      摘要:前端芝士樹中的閉包是怎么一回事筆試問(wèn)題集錦為什么會(huì)有閉包的出現(xiàn)這涉及到作為變量聲明的關(guān)鍵詞時(shí)所出現(xiàn)的一些問(wèn)題。另一方面,在函數(shù)外部自然無(wú)法讀取函數(shù)內(nèi)的局部變量。解決方法是,在退出函數(shù)之前,將不使用的局部變量全部刪除。 【前端芝士樹】Js中的閉包是怎么一回事 && 筆試問(wèn)題集錦 為什么會(huì)有閉包的出現(xiàn)? 這涉及到var作為變量聲明的關(guān)鍵詞時(shí)所出現(xiàn)的一些問(wèn)題。比如,var 的 變量提升 以及...

      awokezhou 評(píng)論0 收藏0

    發(fā)表評(píng)論

    0條評(píng)論

    最新活動(dòng)
    閱讀需要支付1元查看
    <