成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

JavaScript閉包怎么理解

這篇文章主要介紹“JavaScript閉包怎么理解”,在日常操作中,相信很多人在JavaScript閉包怎么理解問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”JavaScript閉包怎么理解”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

高港網(wǎng)站制作公司哪家好,找成都創(chuàng)新互聯(lián)!從網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、APP開發(fā)、響應(yīng)式網(wǎng)站設(shè)計(jì)等網(wǎng)站項(xiàng)目制作,到程序開發(fā),運(yùn)營維護(hù)。成都創(chuàng)新互聯(lián)公司2013年成立到現(xiàn)在10年的時(shí)間,我們擁有了豐富的建站經(jīng)驗(yàn)和運(yùn)維經(jīng)驗(yàn),來保證我們的工作的順利進(jìn)行。專注于網(wǎng)站建設(shè)就選成都創(chuàng)新互聯(lián)

JavaScript閉包怎么理解

從定義上來講,它是一個(gè)腳本語言,而且是一個(gè)相對(duì)容易學(xué)習(xí)的腳本語言。不需要太多的專業(yè)知識(shí),你也能夠在一定程度上使用js(JavaScript的簡寫)代碼。

1. 學(xué)習(xí)JavaScript的契機(jī)

正式學(xué)習(xí)JavaScript是在培訓(xùn)班,沒錯(cuò)我是從培訓(xùn)班出來的,并不是科班出身,可以說是非常的草根了。我學(xué)習(xí)的時(shí)候ES6標(biāo)準(zhǔn)還并未普及,變量命名還在用非常傳統(tǒng)的var,學(xué)習(xí)的第一段代碼是經(jīng)典的console.log('Hello,world!'),當(dāng)然它是在控制臺(tái)上打印出來的。

當(dāng)然,在培訓(xùn)機(jī)構(gòu)中的JavaScript內(nèi)容講的是非常的淺顯,只有最為基礎(chǔ)的變量定義與命名,function聲明,回調(diào)函數(shù),ajax以及最為基礎(chǔ)的dom操作。顯然這些內(nèi)容對(duì)于工作完全不夠用的。

對(duì)于js學(xué)習(xí)的‘進(jìn)修’機(jī)會(huì)來源于我的工作,在工作中我第一次知道了node這個(gè)東西,也了解到即便是js也是可以做后臺(tái)的(我是做的JAVA培訓(xùn)),也開始逐漸接觸到了一些ES6的標(biāo)準(zhǔn)。當(dāng)然這些都是后話,最開始我接觸到最大的障礙是這貨。

2.‘惡心’的閉包

啊,對(duì)我只有那么一丁丁點(diǎn)基礎(chǔ)的我,完全無法理解我們公司自己封裝的jsonp代碼,它是長這個(gè)樣子的。

  var jsonp = (function(){
        var JSONP;
       return function(url){
           if (JSONP) {
             document.getElementsByTagName("head")[0].removeChild(JSONP);
          }
         JSONP = document.createElement("script");
          JSONP.type = "text/javascript";
          JSONP.src = url;
          document.getElementsByTagName("head")[0].appendChild(JSONP);
       }
     }())

當(dāng)然,現(xiàn)在瀏覽器上已經(jīng)無法通過控制臺(tái)直接使用這個(gè)方法了,為了防止XSS攻擊瀏覽器已經(jīng)禁止這樣注入代碼了,但是在服務(wù)器上還是可以用的,當(dāng)然,這些都不是重點(diǎn)。

重點(diǎn)是這里

    if (JSONP) {
       //dosome
 }

如果你和我當(dāng)初一樣,不知道什么叫閉包或者對(duì)閉包一知半解,那么,對(duì)于這里你應(yīng)該也會(huì)產(chǎn)生疑問,思路大約是這樣的

第2行定義了JSONP但是沒有賦值,現(xiàn)在JSONP值為null,第三行返回了一個(gè)方法,第四行檢測JSONP值是否為空,如果不為空則做了一些事情,好了,后面可以不用看了,這個(gè)if白寫了,它百分百進(jìn)不去!

你看嘛,前面也沒有賦值,然后直接判斷,那它明明就是null。但是實(shí)際使用的時(shí)候你會(huì)發(fā)現(xiàn),這個(gè)地方第一次調(diào)用確實(shí)不會(huì)進(jìn)入這個(gè)分支,但只要你調(diào)用了第二次,,它就百分百會(huì)進(jìn)入這個(gè)分支。

// 這個(gè)是一個(gè)可以在控制臺(tái)輸出的閉包版本,你可以自己試一下
var closedhull = (function() {
    let name = null; // 這里直接賦值為null
    return function(msg){
        if(name) {
            console.log('name:', name)
            return name += msg;
        }
        return name = msg;
    }
}())
closedhull('我是第一句。') //我是第一句。
closedhull('我是第二句。') //我是第一句。我是第二句。

上面這個(gè)例子運(yùn)行后,無論是從console.log()亦或是返回值上都不難看出,確實(shí)進(jìn)入了if(name)的分支,這個(gè)就是閉包的表現(xiàn)。這里給出一下閉包的定義

閉包就是能夠讀取其他函數(shù)內(nèi)部變量的函數(shù)。

3.閉包的樣子到底是什么樣的

好了,看過閉包是個(gè)啥了,先不說會(huì)不會(huì)用,至少,算是見過了,閉包有個(gè)顯著的特征return function(){}

不是!

它的顯著特征是在function內(nèi)的function!

觀察以下方法

/*第一個(gè)案例*/
function test1(){
    // a應(yīng)該在方法運(yùn)行結(jié)束后銷毀
    let a = 1;
    return {
        add: function(){
            return ++a;
        }
    }
}
let a = test1();
a.add()//2
a.add()//3
/*第二個(gè)案例*/
(function(){
    // b應(yīng)該在方法運(yùn)行結(jié)束后銷毀
    let b = 1,
        timer = setInterval(()=>{
        console.log(++b)
    }, 2000)
    setTimeout(()=>{
        clearInterval(timer)
    }, 10000)
})()// 2 3 4 5 6
/*第三個(gè)案例*/
function showMaker(obj){
    // obj應(yīng)該在方法運(yùn)行結(jié)束后銷毀
    return function(){
        console.log(JSON.stringify(obj))
    }
}
let shower = showMaker({a:1})
// 顯然這里你還能看到他
shower(); // {"a":1}
/*第四個(gè)案例*/
let outObj = (function(){
    let c = 'hello',
        obj = {};
    Object.defineProperty(obj, 'out', {
        get(){
            return c;
        },
        set(v){
            c = v;
        }
    });
    return obj
})()
outObj.out // 可以讀取并設(shè)置c的值

這四個(gè)都是閉包,他們都具備方法中的方法這一特性。

4.閉包與方法棧(對(duì)原理不感興趣可以略過)

閉包的定義,1. 可以在變量的作用域外訪問該變量。2. 通過某種手段延長一個(gè)局部變量的生命周期。3. 讓一個(gè)局部變量的存活時(shí)間超過它的時(shí)間循環(huán)執(zhí)行時(shí)間

3中由于涉及到了事件循環(huán)概念,之后涉及到時(shí)會(huì)去講的,這里主要討論前兩種方式的定義。

一下內(nèi)容如果你知道方法棧是個(gè)啥了就可以跳過了

局部作用域:在ES6之前,一般指一個(gè)方法內(nèi)部(從參數(shù)列表開始,到方法體的括號(hào)結(jié)束為止),ES6中增加let關(guān)鍵字后,在使用let的情況下是指在一個(gè){}中的范圍內(nèi)(顯然,你不能在隱式的{}中使用let,編譯器會(huì)禁止你做出這種行為的,因?yàn)闆]有{}就沒有塊級(jí)作用域),咱們這里為了簡化討論內(nèi)容,暫且不把let的塊級(jí)作用域算作閉包的范疇(其實(shí)應(yīng)該算,不過意義不大,畢竟,你可以在外層塊聲明它。天啊,JS的命名還沒擁擠到需要在一個(gè)方法內(nèi)再去防止污染的程度。)

局部變量:區(qū)別于全局變量,全局變量會(huì)在某些時(shí)候被意外額創(chuàng)造和使用,這令人非常的...惱火和無助。局部變量就是在局部作用域下使用變量聲明關(guān)鍵字聲明出來的變量,應(yīng)該很好理解。

局部變量的生命周期:好了,你在一個(gè)局部作用域中通過關(guān)鍵字(var const let等)聲明了一個(gè)變量,然后給它賦值,這個(gè)局部變量在這個(gè)局部作用域中冒險(xiǎn)就開始了,它會(huì)被使用,被重新賦值(除了傲嬌的const小姐外),被調(diào)用(如果它是個(gè)方法),這個(gè)局部變量的本質(zhì)是一個(gè)真實(shí)的值,區(qū)別在于如果它是個(gè)對(duì)象(對(duì)象,數(shù)組,方法都是對(duì)象)那么,它其實(shí)本質(zhì)是一個(gè)地址的指針。如果它一個(gè)基礎(chǔ)類型,那么它就是那個(gè)真實(shí)的值。它之所以存活是因?yàn)樗袀€(gè)住所。內(nèi)存。

局部作用域與內(nèi)存:每當(dāng)出現(xiàn)一個(gè)局部作用域,一個(gè)方法棧就被申請(qǐng)了出來,在這個(gè)方法棧大概長這樣子

|  data5 |
|  data4 |
|  data3 |
|  data2 |
|__data1_|

當(dāng)然,它是能夠套娃的,長這個(gè)樣子

|  | d2 |  |
|  |_d1_|  |
|  data3   |
|  data2   |
|__data1___|

如果上面的東西是在太過于抽象,那么,我可以用實(shí)際案例展示一下

function stack1(){
    var data1,
        data2,
        data3,
        data4,
        data5
}
function stack2(){
    var data1,
        data2,
        data3;
    function stackInner(){
        var d1,
            d2;
    }
}

如果方法棧能夠直觀的感受的話,大約就是這個(gè)樣子,咱們重點(diǎn)來分析stack2的這種情況,同時(shí)寫一點(diǎn)實(shí)際內(nèi)容進(jìn)去

function stack2(){
    var data1 = '1',
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = '4',
            d2 = {y: '5'};
    }
    stackInner()
}
stack2()

顯然其中data1,data3,d1持有的是基本類型(string),data2,d2持有的是引用類型(object),反應(yīng)到圖上

運(yùn)行時(shí)的方法棧的樣子

            |------>{y: '5'}
            |    |->{x: '2'}
    |  | d2-|   || |
    |  |_d1='4'_|| |
    |  data3='3' | |
    |  data2 ----| |
    |__data1='1'___|

畫有點(diǎn)抽象...就這樣吧。具體對(duì)象在哪呢?他們在一個(gè)叫堆的地方,不是這次的重點(diǎn),還是先看方法棧內(nèi)的這些變量,運(yùn)行結(jié)束后,按照先進(jìn)后出的原則,把棧內(nèi)的局部變量一個(gè)一個(gè)的銷毀,同時(shí)堆里的兩個(gè)對(duì)象,由于引用被銷毀,沒了繼續(xù)存在的意義,等待被垃圾回收。

接下來咱們要做兩件事情:

  • d1不再等于4了,而是引用data1

  • return stackInner 而不是直接調(diào)用

這樣閉包就完成了

function stack2(){
    var data1 = {msg: 'hello'},
        data2 = {x: '2'},
        data3 = '3';
    function stackInner(){
        var d1 = data1,
            d2 = {y: '5'};
    }
    return stackInner
}
var out = stack2()

這里有一個(gè)要點(diǎn),d2賦值給data1一定是在stackInner中完成的,原因?因?yàn)樵賡tackInner方法中d2才被聲明出來,如果你在stack2中d1 = data1那么恭喜你,你隱式的聲明了一個(gè)叫d1的全局變量,而且在stackInner由于變量屏蔽的原因,你也看不到全局上的d2,原本計(jì)劃的閉包完全泡湯。

變量屏蔽:不同作用域中相同名稱的變量就會(huì)觸發(fā)變量屏蔽。

看看棧現(xiàn)在的樣子

運(yùn)行時(shí)的方法棧的樣子

               |------>{y: '5'}
out<---|       | |----|
    |  |  | d2-| | |  |  |
    |  |--|_d1---|_|  |  |
    |     data3='3'   |  |
    |     data2(略)   |  |
    |_____data1<------|__|

好了,這個(gè)圖可以和我們永別了,如果有可能,我后面會(huì)用畫圖工具替代,這么畫圖實(shí)在是太過邪典了。

這里涉及到了方法棧的一個(gè)特性,就是變量的穿透性,外部變量可以在內(nèi)部的任意位置使用,因?yàn)樵賰?nèi)部執(zhí)行結(jié)束前,外部變量會(huì)一直存在。

由于stackInner被外部的out引用,導(dǎo)致這個(gè)對(duì)象不會(huì)隨著方法棧的結(jié)束而銷毀,接下來,最神奇的事情來了,由于stackInner這對(duì)象沒有銷毀,它內(nèi)部d1依然保有data1所對(duì)應(yīng)數(shù)據(jù)的引用,d1,d2一定會(huì)活下來,因?yàn)樗麄兊陌职謘tackInner活下來了,data1也以某種形式活了下來。

為什么說是某種形式,因?yàn)椋举|(zhì)上來說data1還是被銷毀了。沒錯(cuò),只不過,data1所引用的那個(gè)對(duì)象的地址鏈接沒有被銷毀,這個(gè)才是本質(zhì)。棧在調(diào)用結(jié)束后一定是會(huì)銷毀的。但是調(diào)用本體(方法對(duì)象)只要存在,那么內(nèi)部所引用的鏈接就不會(huì)斷。

這個(gè)就是閉包的成因和本質(zhì)。

5.閉包有什么用

OK,我猜測上一個(gè)章節(jié)估計(jì)很多人都直接跳過了,其實(shí),跳過影響也不多,這個(gè)部分描述一下結(jié)論性的東西,閉包的作用。

它的最大作用就是給你的變量一個(gè)命名空間,防止命名沖突。要知道,你的框架,你export的東西,你import進(jìn)來的東西,在編譯的時(shí)候都會(huì)變成閉包,為的就是減少你變量對(duì)全局變量的污染,一個(gè)不依賴與import export的模塊的代碼大概長這個(gè)樣子

(function(Constr, global){
    let xxx = new Constr(env1, env2, env3)
    global.NameSpace = xxx;
})(function(parm1, parm2, parm3) {
    //dosomeing
    reutrn {
        a: 'some1',
        b: 'some2',
        funcC(){
            //dosome
        },
        funcD(){
            //dosome
        }
    }
}, window)

當(dāng)然這種封裝代碼的風(fēng)格有多種多樣的,但是大家都盡量把一套體系的內(nèi)容都放到一個(gè)命名空間下,避免與其他框架產(chǎn)生沖突

到此,關(guān)于“JavaScript閉包怎么理解”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

本文名稱:JavaScript閉包怎么理解
網(wǎng)頁網(wǎng)址:http://jinyejixie.com/article4/gpshoe.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司定制開發(fā)、營銷型網(wǎng)站建設(shè)、軟件開發(fā)企業(yè)網(wǎng)站制作、做網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

營銷型網(wǎng)站建設(shè)
关岭| 思南县| 丹棱县| 高雄县| 勃利县| 西峡县| 甘肃省| 祥云县| 金堂县| 明光市| 综艺| 衢州市| 陕西省| 湛江市| 壶关县| 富源县| 蛟河市| 霞浦县| 湘潭县| 北辰区| 宣城市| 泸溪县| 海南省| 皋兰县| 海南省| 鹿泉市| 三台县| 秭归县| 上高县| 文化| 新巴尔虎左旗| 筠连县| 北辰区| 南木林县| 桐梓县| 泰宁县| 绥滨县| 富源县| 大庆市| 万州区| 社会|