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

資訊專(zhuān)欄INFORMATION COLUMN

JavaScript 中的閉包

liuhh / 2688人閱讀

摘要:簡(jiǎn)要介紹閉包可謂是中的一大特色了,即使你對(duì)閉包沒(méi)概念,你可能已經(jīng)在不知不覺(jué)中使用到了閉包。這就是閉包的獨(dú)特之處。當(dāng)頁(yè)面中存在過(guò)多的閉包,或者閉包的嵌套很多很深時(shí),會(huì)導(dǎo)致內(nèi)存占用過(guò)多。因此,在這里建議慎用閉包。

1. 簡(jiǎn)要介紹

閉包可謂是js中的一大特色了,即使你對(duì)閉包沒(méi)概念,你可能已經(jīng)在不知不覺(jué)中使用到了閉包。閉包是什么,閉包就是一個(gè)函數(shù)可以訪問(wèn)到另一個(gè)函數(shù)的變量。這就是閉包,解釋起來(lái)就這么一句話,不明白?我們來(lái)看一個(gè)簡(jiǎn)單的例子:

function getName(){
    var name="wenzi";
    setTimeout(function(){
        console.log(name);
    }, 500);
}
getName();

這就其實(shí)已經(jīng)是閉包了,setTimeout中的function是一個(gè)匿名函數(shù),這個(gè)匿名函數(shù)里的name是geName()作用域中的變量,匿名函數(shù)里只有一個(gè)輸出語(yǔ)句:console.log();

還有一個(gè)很經(jīng)典的例子也可以幫助我們理解什么是閉包:

function create(){
    var i=0;
    // 返回一個(gè)函數(shù),暫且稱之為函數(shù)A
    return function(){
        i++;
        console.log(i);
    }
}
var c = create(); // c是一個(gè)函數(shù)
c(); // 函數(shù)執(zhí)行
c(); // 再次執(zhí)行
c(); // 第三次執(zhí)行

在上面的例子中,create()返回的是一個(gè)函數(shù),我們暫且稱之為函數(shù)A吧。在函數(shù)A中,有兩條語(yǔ)句,一條是變量i自增(i++),一條是輸出語(yǔ)句(console.log)。第一次執(zhí)行執(zhí)行c()時(shí)會(huì)產(chǎn)生什么樣的結(jié)果?嗯,輸出自增后的變量i,也就是輸出1;那么第二次執(zhí)行c()呢,對(duì),會(huì)輸出2;第三次執(zhí)行c()時(shí)會(huì)輸出3,依次累加。這個(gè)create()函數(shù)依然滿足了我們?cè)趧傞_(kāi)始時(shí)的定義,函數(shù)A使用到了另一個(gè)函數(shù)create()中的變量i。

可是為什么會(huì)產(chǎn)生這樣的輸出呢,為什么i就能一直自增呢,create函數(shù)已經(jīng)執(zhí)行完并返回結(jié)果了呀,可是為什么還能接著使用i呢,而且i還能自增。這里就涉及到了三個(gè)比較重要的概念,講解完這三個(gè)概念,我們對(duì)閉包就可以有一個(gè)比較好的理解了。

2. 三個(gè)重要概念 2.1 執(zhí)行環(huán)境與變量對(duì)象

執(zhí)行環(huán)境是JavaScript中一個(gè)重要的概念,它決定了變量或函數(shù)是否有權(quán)訪問(wèn)其他的數(shù)據(jù),決定了它們各自的行為。每個(gè)執(zhí)行環(huán)境都有一個(gè)與之對(duì)應(yīng)的變量對(duì)象,執(zhí)行環(huán)境中定義的所有變量和函數(shù)都保存在這個(gè)對(duì)象中。雖然我們的代碼無(wú)法訪問(wèn)這個(gè)對(duì)象,但是解析器在處理數(shù)據(jù)時(shí)會(huì)在后臺(tái)使用它。

我們用一個(gè)比較簡(jiǎn)單的比喻來(lái)形容這兩個(gè)概念。執(zhí)行環(huán)境就是一個(gè)人,變量對(duì)象就是這個(gè)人的身份證號(hào),每個(gè)人都有其對(duì)應(yīng)的身份證號(hào)。他這個(gè)人決定了他身上的很多屬性和方法(動(dòng)作),而且這個(gè)人的屬性和方法都在他的身份證號(hào)上,當(dāng)這個(gè)人消亡的時(shí)候,身份證號(hào)也就隨之就注銷(xiāo)了。

全局執(zhí)行環(huán)境是最外層的一個(gè)執(zhí)行環(huán)境。在web瀏覽器中,全局執(zhí)行環(huán)境被認(rèn)為是window對(duì)象,因?yàn)樗械娜肿兞亢腿趾瘮?shù)都是作為window對(duì)象的屬性和方法創(chuàng)建的。某個(gè)執(zhí)行環(huán)境中的所有代碼執(zhí)行完畢后,該環(huán)境被銷(xiāo)毀,保存在其中的所有變量和函數(shù)定義也隨之銷(xiāo)毀(全局執(zhí)行環(huán)境直到應(yīng)用程序退出——例如關(guān)閉網(wǎng)頁(yè)或者瀏覽器——時(shí)才會(huì)被銷(xiāo)毀),被垃圾回收機(jī)制回收。

每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境。當(dāng)執(zhí)行流進(jìn)入一個(gè)函數(shù)時(shí),函數(shù)的環(huán)境就會(huì)被推入到一個(gè)環(huán)境棧中。而在函數(shù)執(zhí)行后,棧將其環(huán)境彈出,把控制權(quán)返回給之前的執(zhí)行環(huán)境。

2.2 作用域鏈

作用域鏈?zhǔn)钱?dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí)創(chuàng)建的,作用域鏈的用途就是要保證執(zhí)行環(huán)境中能有效有序的訪問(wèn)所有變量和函數(shù)。作用域鏈的最前端始終都是當(dāng)前執(zhí)行的代碼所在環(huán)境的變量對(duì)象,下一個(gè)變量對(duì)象是來(lái)自其父親環(huán)境,再下一個(gè)變量對(duì)象是其父親的父親環(huán)境,直到全局執(zhí)行環(huán)境。

標(biāo)識(shí)符解析是沿著作用域鏈一級(jí)一級(jí)地搜索標(biāo)識(shí)符的過(guò)程。搜索過(guò)程始終從作用域鏈的前端開(kāi)始,然后逐級(jí)地向后回溯,直到找到標(biāo)識(shí)符為止(如果找不到標(biāo)識(shí)符,通常會(huì)導(dǎo)致錯(cuò)誤發(fā)生)。其實(shí),通俗的理解就是:在本作用域內(nèi)找不到變量或者函數(shù),則在其父親的作用域內(nèi)尋找,再找不到則到父親的父親作用域內(nèi)尋找,直到在全局的作用域內(nèi)尋找!

2.3 垃圾回收機(jī)制

在js中有兩種垃圾收集的方式:標(biāo)記清除和引用計(jì)數(shù)。

標(biāo)記清除:垃圾收集器在運(yùn)行時(shí)會(huì)給存儲(chǔ)在內(nèi)存中的所有變量都加上標(biāo)記(具體的標(biāo)記方式暫時(shí)就不清楚了),待變量已不被使用或者引用,去掉該標(biāo)記或添加另一種標(biāo)記。最后,垃圾收集器完成內(nèi)存清除工作,銷(xiāo)毀那些已無(wú)法訪問(wèn)到的這些變量并回收他們所占用的空間。

引用計(jì)數(shù):一般來(lái)說(shuō),引用計(jì)數(shù)的含義是跟蹤記錄每個(gè)值被引用的次數(shù)。當(dāng)聲明一個(gè)變量并將一個(gè)引用類(lèi)型值賦給該變量時(shí),則這個(gè)值的引用次數(shù)便是1,如果同一個(gè)值又被賦給另一個(gè)變量,則該值的引用次數(shù)加1,相反,如果包含對(duì)這個(gè)值引用的變量又取得了另一個(gè)值,則這個(gè)值的引用次數(shù)減1。當(dāng)這個(gè)值的引用次數(shù)為0時(shí),說(shuō)明沒(méi)有辦法訪問(wèn)到它了,因而可以將其占用的內(nèi)存空間回收。

除了一些極老版本的IE,目前市面上的JS引擎基本采用標(biāo)記清除來(lái)除了垃圾回收。但是需要注意的是IE中的DOM由于機(jī)制問(wèn)題,是采用了引用計(jì)數(shù)的方式,所以會(huì)有循環(huán)引用的問(wèn)題,造成內(nèi)存泄露。

var ele = document.getElementById(“element”);
var obj = new Object();
ele.obj = obj; // DOM元素ele的obj引用obj變量
obj.ele = ele; // obj變量的ele引用了DOM元素ele

這樣就造成了循環(huán)引用的問(wèn)題,導(dǎo)致垃圾回收機(jī)制回收不了ele和obj。不過(guò),可以在不使用ele和obj時(shí),對(duì)這兩個(gè)變量進(jìn)行 null 賦值,然后垃圾回收機(jī)制就會(huì)回收它們了。

3. 理解閉包

在第2部分講解了三個(gè)重要的概念,這三個(gè)概念有助于我們更好的理解閉包。

我們?cè)俅文贸錾厦娴倪@個(gè)例子:

function create(){
    var i=0;
    // 返回一個(gè)函數(shù),暫且稱之為函數(shù)A
    return function(){
        i++;
        console.log(i);
    }
}
var c = create(); // c是一個(gè)函數(shù),即函數(shù)A
c(); // 函數(shù)執(zhí)行
c(); // 再次執(zhí)行
c(); // 第三次執(zhí)行

從上面的“每個(gè)函數(shù)都有自己的執(zhí)行環(huán)境”可以知道:create()函數(shù)是一個(gè)執(zhí)行環(huán)境,函數(shù)A也是一個(gè)執(zhí)行環(huán)境,且函數(shù)A的執(zhí)行環(huán)境在create()的里面。這樣就形成了一個(gè)作用域鏈:window->create->A。當(dāng)執(zhí)行c()時(shí),函數(shù)A就會(huì)首先在當(dāng)前執(zhí)行環(huán)境中尋找變量i,可是沒(méi)有找到,那么只能順著作用域鏈向后找;OK,在create()的執(zhí)行環(huán)境中找到了,那么就可以使用了變量i了。

可是我們還有一個(gè)疑問(wèn),按照上面的說(shuō)法,函數(shù)create()執(zhí)行完畢后,這個(gè)函數(shù)與里面的變量和方法應(yīng)該被銷(xiāo)毀了呀,可是為什么函數(shù)c()多次執(zhí)行時(shí)依然能夠輸出變量i呢。這就是閉包的獨(dú)特之處。

函數(shù)create()執(zhí)行完畢后,雖然它的作用域鏈會(huì)被銷(xiāo)毀,即不再存在window->create這個(gè)鏈?zhǔn)疥P(guān)系,但是函數(shù)A()[c()]的作用域鏈還依然引用著create()的變量對(duì)象,還存在著window->create->A的鏈?zhǔn)疥P(guān)系,導(dǎo)致垃圾回收機(jī)制不能回收create()的變量對(duì)象,create()的變量對(duì)象仍然停留在內(nèi)存中,直到函數(shù)A()[c()]被銷(xiāo)毀后,create()的變量對(duì)象才會(huì)被銷(xiāo)毀。

因此,雖然create()已經(jīng)執(zhí)行完畢了,但是create()的變量對(duì)象并沒(méi)有被回收,還停留在內(nèi)存中,依然可以使用。

從上面的講解中我們可以看到,閉包會(huì)攜帶包含它的函數(shù)的作用域,因此會(huì)比其他函數(shù)占用更多的內(nèi)存。當(dāng)頁(yè)面中存在過(guò)多的閉包,或者閉包的嵌套很多很深時(shí),會(huì)導(dǎo)致內(nèi)存占用過(guò)多。因此,在這里建議:慎用閉包

4. 閉包與變量

有很多新手為DOM元素綁定事件時(shí),通常會(huì)這么寫(xiě):

function bindClick(){
    var li = document.getElementsByTagName("li"); // 假設(shè)一共有5個(gè)li標(biāo)簽
    for(var i=0; i

他的本意是想為每個(gè)li標(biāo)簽綁定一個(gè)多帶帶的事件,點(diǎn)擊第幾個(gè)li標(biāo)簽,就能輸出幾。可是,最后的結(jié)果卻是,點(diǎn)擊哪個(gè)li標(biāo)簽輸出的都是5,這是為什么呢?

其實(shí)這位程序員寫(xiě)的bindClick()已經(jīng)構(gòu)成了一個(gè)閉包,下面的這個(gè)函數(shù)有他的作用域,而變量i本不屬于這個(gè)函數(shù)的作用域,而是屬于bindClick()中的:

// 匿名函數(shù)
function(){
    console.log("click the "+i+" li tag");
}

因此,這就構(gòu)成了一個(gè)含有閉包的作用域鏈:window->bindClick->匿名函數(shù)??墒沁@跟輸出的i有關(guān)系么?有。作用域鏈中的每個(gè)變量對(duì)象保存的是對(duì)變量和方法的引用,而不是保存這個(gè)變量的某一個(gè)值。當(dāng)執(zhí)行到匿名函數(shù)時(shí),bindClick()其實(shí)已經(jīng)執(zhí)行完畢了,變量i的值就是5,此時(shí)每個(gè)匿名函數(shù)都引用著同一個(gè)變量i。

不過(guò)我們稍微修改一下,以滿足我們的預(yù)期:

/* 
    // 錯(cuò)誤,onclick綁定的是立即執(zhí)行函數(shù)的返回值,
    // 而此立即執(zhí)行并沒(méi)有返回值,也就是onclick = undefined
    function bindClick(){
    var li = document.getElementsByTagName("li");
    for(var i=0; i

在這里,我們使用立即執(zhí)行的匿名函數(shù)來(lái)保證傳入的值就是當(dāng)前正在操作的變量i,而不是循環(huán)完成后的值。

5. 閉包的應(yīng)用場(chǎng)景

(1)在內(nèi)存中維持一個(gè)變量。比如前面講的小例子,由于閉包,函數(shù)create()中的變量i會(huì)一直存在于內(nèi)存中,因此每次執(zhí)行c(),都會(huì)給變量i加1.

(2)保護(hù)函數(shù)內(nèi)的變量安全。還是那個(gè)小例子,函數(shù)create()中的變量c只有內(nèi)部的函數(shù)才能訪問(wèn),而無(wú)法通過(guò)其他途徑訪問(wèn)到,因此保護(hù)了變量c的安全。

(3)實(shí)現(xiàn)面向?qū)ο笾械膶?duì)象。javascript并沒(méi)有提供類(lèi)這樣的機(jī)制,但是我們可以通過(guò)閉包來(lái)模擬出類(lèi)的機(jī)制,不同的對(duì)象實(shí)例擁有獨(dú)立的成員和狀態(tài)。

這里我們看一個(gè)例子:

function Student(){
    var name = "wenzi";

    return {
        setName : function(na){
            name = na;
        },

        getName : function(){
            return name;
        }
    }
}
var stu = new Student();
console.log(stu.name); // undefined
console.log(stu.getName()); // wenzi

這就是一個(gè)用閉包實(shí)現(xiàn)的簡(jiǎn)單的類(lèi),里面的name屬性是私有的,外部無(wú)法進(jìn)行訪問(wèn),只能通過(guò)setName和getName進(jìn)行訪問(wèn)。

當(dāng)然,閉包還存在另外一種形式:

var a = (function(){
    var num = 0;
    return function(){
        return num++;
    }
})()
a(); // 0
a(); // 1
a(); // 2

本人個(gè)人博客:http://www.xiabingbao.com

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

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

相關(guān)文章

  • JavaScript中的閉包

    摘要:閉包引起的內(nèi)存泄漏總結(jié)從理論的角度將由于作用域鏈的特性中所有函數(shù)都是閉包但是從應(yīng)用的角度來(lái)說(shuō)只有當(dāng)函數(shù)以返回值返回或者當(dāng)函數(shù)以參數(shù)形式使用或者當(dāng)函數(shù)中自由變量在函數(shù)外被引用時(shí)才能成為明確意義上的閉包。 文章同步到github js的閉包概念幾乎是任何面試官都會(huì)問(wèn)的問(wèn)題,最近把閉包這塊的概念梳理了一下,記錄成以下文章。 什么是閉包 我先列出一些官方及經(jīng)典書(shū)籍等書(shū)中給出的概念,這些概念雖然...

    HmyBmny 評(píng)論0 收藏0
  • JavaScript閉包,只學(xué)這篇就夠了

    摘要:當(dāng)在中調(diào)用匿名函數(shù)時(shí),它們用的都是同一個(gè)閉包,而且在這個(gè)閉包中使用了和的當(dāng)前值的值為因?yàn)檠h(huán)已經(jīng)結(jié)束,的值為。最好將閉包當(dāng)作是一個(gè)函數(shù)的入口創(chuàng)建的,而局部變量是被添加進(jìn)這個(gè)閉包的。 閉包不是魔法 這篇文章使用一些簡(jiǎn)單的代碼例子來(lái)解釋JavaScript閉包的概念,即使新手也可以輕松參透閉包的含義。 其實(shí)只要理解了核心概念,閉包并不是那么的難于理解。但是,網(wǎng)上充斥了太多學(xué)術(shù)性的文章,對(duì)于...

    CoderBear 評(píng)論0 收藏0
  • 還擔(dān)心面試官問(wèn)閉包?

    摘要:一言以蔽之,閉包,你就得掌握。當(dāng)函數(shù)記住并訪問(wèn)所在的詞法作用域,閉包就產(chǎn)生了。所以閉包才會(huì)得以實(shí)現(xiàn)。從技術(shù)上講,這就是閉包。執(zhí)行后,他的內(nèi)部作用域并不會(huì)消失,函數(shù)依然保持有作用域的閉包。 網(wǎng)上總結(jié)閉包的文章已經(jīng)爛大街了,不敢說(shuō)筆者這篇文章多么多么xxx,只是個(gè)人理解總結(jié)。各位看官瞅瞅就好,大神還希望多多指正。此篇文章總結(jié)與《JavaScript忍者秘籍》 《你不知道的JavaScri...

    tinyq 評(píng)論0 收藏0
  • JavaScript中的閉包

    摘要:權(quán)威指南第版中閉包的定義函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中成為閉包。循環(huán)中的閉包使用閉包時(shí)一種常見(jiàn)的錯(cuò)誤情況是循環(huán)中的閉包,很多初學(xué)者都遇到了這個(gè)問(wèn)題。 閉包簡(jiǎn)介 閉包是JavaScript的重要特性,那么什么是閉包? 《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》中閉包的定義: 閉包就是指有權(quán)訪問(wèn)另一個(gè)函數(shù)中的變...

    Donne 評(píng)論0 收藏0
  • 深入javascript——作用域和閉包

    摘要:注意由于閉包會(huì)額外的附帶函數(shù)的作用域內(nèi)部匿名函數(shù)攜帶外部函數(shù)的作用域,因此,閉包會(huì)比其它函數(shù)多占用些內(nèi)存空間,過(guò)度的使用可能會(huì)導(dǎo)致內(nèi)存占用的增加。 作用域和作用域鏈?zhǔn)莏avascript中非常重要的特性,對(duì)于他們的理解直接關(guān)系到對(duì)于整個(gè)javascript體系的理解,而閉包又是對(duì)作用域的延伸,也是在實(shí)際開(kāi)發(fā)中經(jīng)常使用的一個(gè)特性,實(shí)際上,不僅僅是javascript,在很多語(yǔ)言中都...

    oogh 評(píng)論0 收藏0
  • Javascript閉包入門(mén)(譯文)

    摘要:也許最好的理解是閉包總是在進(jìn)入某個(gè)函數(shù)的時(shí)候被創(chuàng)建,而局部變量是被加入到這個(gè)閉包中。在函數(shù)內(nèi)部的函數(shù)的內(nèi)部聲明函數(shù)是可以的可以獲得不止一個(gè)層級(jí)的閉包。 前言 總括 :這篇文章使用有效的javascript代碼向程序員們解釋了閉包,大牛和功能型程序員請(qǐng)自行忽略。 譯者 :文章寫(xiě)在2006年,可直到翻譯的21小時(shí)之前作者還在完善這篇文章,在Stackoverflow的How do Java...

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

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

0條評(píng)論

liuhh

|高級(jí)講師

TA的文章

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