成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

JavaScript中的閉包

Donne / 468人閱讀

摘要:權(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ù)中的變量的函數(shù)。

《JavaScript權(quán)威指南(第6版)》中閉包的定義:

函數(shù)對(duì)象可以通過(guò)作用域鏈相互關(guān)聯(lián)起來(lái),函數(shù)體內(nèi)部的變量都可以保存在函數(shù)作用域內(nèi),這種特性在計(jì)算機(jī)科學(xué)文獻(xiàn)中成為“閉包”。

簡(jiǎn)單來(lái)說(shuō),在JavaScript中,函數(shù)是對(duì)象,對(duì)象是屬性的集合,屬性的值也可以是對(duì)象,在函數(shù)內(nèi)定義函數(shù)就成為一種常見(jiàn)的情況,在函數(shù)內(nèi)部聲明函數(shù)innerFunction,在innerFunction內(nèi)部有權(quán)訪問(wèn)外部函數(shù)的變量對(duì)象,這個(gè)函數(shù)就是我們所說(shuō)的閉包。

我們來(lái)看一個(gè)簡(jiǎn)單的例子:

function checkScope(){
    var scope = "local scope";
    function f() { return scope; }
    return f();
}
checkScope();//輸出為“l(fā)ocal scope”

當(dāng)函數(shù)第一次被調(diào)用時(shí),會(huì)創(chuàng)建一個(gè)執(zhí)行環(huán)境以及相應(yīng)的作用域鏈,作用域鏈的前端,始終都是當(dāng)前執(zhí)行代碼所在環(huán)境的變量對(duì)象,作用域鏈中的下一個(gè)變量對(duì)象來(lái)自包含外部環(huán)境,下一個(gè)變量則來(lái)自下一個(gè)外部環(huán)境,這樣一直延續(xù)到全局執(zhí)行環(huán)境。

在上邊的例子中,訪問(wèn)scope時(shí),內(nèi)部的f()函數(shù)可以訪問(wèn)f()外部的變量scope,因?yàn)樗谧饔糜蜴溨幸患?jí)一級(jí)往上找的時(shí)候可以找到scope變量。

閉包的作用

一、 模擬私有變量。在函數(shù)內(nèi)創(chuàng)建一個(gè)閉包,閉包就可以通過(guò)自己的作用域鏈訪問(wèn)函數(shù)內(nèi)部的變量,可以創(chuàng)建用于訪問(wèn)私有變量的方法。訪問(wèn)私有變量和私有函數(shù)的方法被稱為特權(quán)方法。

function MyObject(){
    var privateVariable = 10;
    function privateFunction() {
        return false;
    }
    //特權(quán)方法
    this.publicFunction = function() {
        privateVariable++;
        return privateFunction();
    };
}

二、 模仿塊級(jí)作用域。 JavaScript中沒(méi)有塊級(jí)作用域的概念,這意味著在塊語(yǔ)句中定義的變量,實(shí)際上是包含在函數(shù)中的。如果臨時(shí)需要一些變量,使用私有作用域。

function block() {
    var a = 1;
    var b = 2;
    (function () {
        var a = 3;//覆蓋了父作用域中的變量a
        var c = 4;
        //訪問(wèn)到了當(dāng)前作用域中的變量
        console.log(a);//3
        //訪問(wèn)了父作用域中的變量
        console.log(b);//2
        //訪問(wèn)當(dāng)前作用域中的變量
        console.log(c);//4
    })()
    //訪問(wèn)塊級(jí)作用域中的變量
    console.log(c);//c is not defined
}

這種技術(shù)經(jīng)常在全局作用域中被用在函數(shù)外部,從而限制向全局作用域中添加過(guò)多的變量和函數(shù)。

循環(huán)中的閉包

使用閉包時(shí)一種常見(jiàn)的錯(cuò)誤情況是循環(huán)中的閉包,很多初學(xué)者都遇到了這個(gè)問(wèn)題。很常見(jiàn)的一種情況就是給頁(yè)面中的多個(gè)按鈕綁定點(diǎn)擊事件,JavaScript代碼如下所示:

window.onload = function(){
    var inputs = document.getElementsByTagName("input");
    for(var i = 0; i < inputs.length; i++){
        inputs[i].onclick = function(){
            console.log(i);//希望輸出0,1,2,3,4...
        }
    }
}

頁(yè)面中有5個(gè)按鈕,根據(jù)上邊的代碼,我們需要的是依次點(diǎn)擊按鈕時(shí),控制臺(tái)分別打印出0,1,2,3,4,而實(shí)際上,控制臺(tái)打印出來(lái)的,如下圖所示:

這是為什么呢,當(dāng)for循環(huán)執(zhí)行完之后,i已經(jīng)變成了按鈕的個(gè)數(shù)5了,而所有點(diǎn)擊函數(shù)綁定的都是同一個(gè)i,點(diǎn)擊按鈕時(shí),打印出來(lái)的i也都變成了5了。

這一部分理解也可以參考http://www.cnblogs.com/qieguo...。

那么我們?yōu)榱说玫较胍慕Y(jié)果,需要在每次循環(huán)中創(chuàng)建變量i的拷貝,下面提供三種方法。

第一、使用匿名包裝器

window.onload = function () {
    var inputs = document.getElementsByTagName("input");
    for (var i = 0; i < inputs.length; i++) {
        (function (e) {
            inputs[i].onclick = function () {
                console.log(e);
            }
        })(i);

    }
}

依次點(diǎn)擊按鈕,控制臺(tái)輸出如下:

第二、從匿名包裝器中返回一個(gè)函數(shù):

window.onload = function () {
    var inputs = document.getElementsByTagName("input");
    for (var i = 0; i < inputs.length; i++) {
        inputs[i].onclick = function (num) {
            return function () {
                console.log(num);
            };
        } (i);
    }
}

首先,定義了匿名函數(shù),并將立即執(zhí)行該匿名函數(shù)的結(jié)果賦值給數(shù)組,匿名函數(shù)有一個(gè)參數(shù)num,在調(diào)用每個(gè)函數(shù)時(shí),我們傳入了變量i,函數(shù)按值傳遞,就將變量i的當(dāng)前值復(fù)制給參數(shù)num。而在這個(gè)匿名函數(shù)內(nèi)部,又創(chuàng)建并返回了一個(gè)訪問(wèn)num的閉包,這樣一來(lái),每個(gè)按鈕點(diǎn)擊函數(shù)都有自己num變量的一個(gè)副本,因此可以輸出各自不同的數(shù)值了。

第三、在循環(huán)中使用let

window.onload = function () {
    var inputs = document.getElementsByTagName("input");
    for (let i = 0; i < inputs.length; i++) {
        inputs[i].onclick = function () {
            console.log(i);
        };
    }
}

let是ES6新增的命令,用法類似于var,但是所聲明的變量只能在let命令所在代碼塊內(nèi)有效。上述代碼中,變量i是let聲明的,當(dāng)前的i只在本輪循環(huán)有效。所以每一次循環(huán)的i其實(shí)都是一個(gè)新的變量。關(guān)于let的用法可參考《ECMAScript 6 入門》中第二章。

內(nèi)存泄漏

產(chǎn)生內(nèi)存泄漏的原因是IE9之前的版本對(duì)JScript對(duì)象和COM對(duì)象使用不同的垃圾收集例程,因此閉包在IE的這些版本中會(huì)導(dǎo)致一些問(wèn)題。(JavaScript垃圾收集機(jī)制可參考《JavaScript高級(jí)程序設(shè)計(jì)(第3版)》4.3) 例如:

function assignHandle() {
    var element = document.getElementById("elementId");
    element.onclick = function () {
        //element的onclick引用了匿名函數(shù),
        //匿名函數(shù)又通過(guò)閉包引用了element,造成了循環(huán)引用
        console.log(element.id);
    };
}

這個(gè)例子中,循環(huán)引用導(dǎo)致了element引用數(shù)至少為1,element所占內(nèi)存就永遠(yuǎn)不會(huì)被回收,從而導(dǎo)致了內(nèi)存泄漏問(wèn)題。要解決這個(gè)問(wèn)題,就需要解除對(duì)DOM對(duì)象的引用,減少引用數(shù),確保正常回收其所占用的內(nèi)存。

引用計(jì)數(shù)的含義是跟蹤記錄每個(gè)值被引用的次數(shù)。當(dāng)聲明了一個(gè)變量并將一個(gè)引用類型值賦給該變量時(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)這個(gè)值了,因而就可以將其占用的內(nèi)存空間回收回來(lái)。

在采用引用計(jì)數(shù)策略的實(shí)現(xiàn)中,出現(xiàn)循環(huán)引用時(shí),由于變量的引用次數(shù)永遠(yuǎn)不會(huì)是0,函數(shù)被多次調(diào)用時(shí),就會(huì)導(dǎo)致大量?jī)?nèi)存得不到回收。 這一部分理解可以參考MDN中JavaScript內(nèi)存管理。

結(jié)語(yǔ)

JavaScript閉包是極其有用的特性,但是由于閉包會(huì)攜帶包含它的函數(shù)的作用域,占用更多內(nèi)存,過(guò)多使用閉包可能會(huì)導(dǎo)致內(nèi)存占用過(guò)多。

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/88242.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)典書籍等書中給出的概念,這些概念雖然...

    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——作用域和閉包

    摘要:注意由于閉包會(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í)際開發(fā)中經(jīng)常使用的一個(gè)特性,實(shí)際上,不僅僅是javascript,在很多語(yǔ)言中都...

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

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

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

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

0條評(píng)論

閱讀需要支付1元查看
<