亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

JavaScript閉包(三)

Anonymous1 / 1029人閱讀

摘要:目錄執(zhí)行環(huán)境與作用域鏈立即執(zhí)行函數(shù)閉包知識點(diǎn)什么是閉包使用閉包的意義與注意點(diǎn)閉包的具體應(yīng)用小結(jié)這是基本語法的函數(shù)部分的第篇文章,主要講述了中比較重要的知識點(diǎn)閉包在講閉包之前,在上一篇函數(shù)二的基礎(chǔ)上,進(jìn)一步深化執(zhí)行環(huán)境和作用域鏈的知識點(diǎn),并補(bǔ)

目錄 1.執(zhí)行環(huán)境與作用域鏈 2. 立即執(zhí)行函數(shù) 3. 閉包知識點(diǎn)
3.1 什么是閉包
3.2 使用閉包的意義與注意點(diǎn)
3.3 閉包的具體應(yīng)用
4. 小結(jié)

這是JavaScript基本語法的函數(shù)部分的第2篇文章,主要講述了JavaScript中比較重要的知識點(diǎn)閉包;
在講閉包之前,在上一篇《JavaScript函數(shù)(二)》的基礎(chǔ)上,進(jìn)一步深化執(zhí)行環(huán)境和作用域鏈的知識點(diǎn),并補(bǔ)充立即執(zhí)行函數(shù)方面的知識;
最后重點(diǎn)探討了有關(guān)閉包的相關(guān)方面;

1. 執(zhí)行環(huán)境與作用域鏈

講閉包之前,首先要清晰了解函數(shù)的執(zhí)行環(huán)境和作用域鏈的原理;
1.1 執(zhí)行環(huán)境
執(zhí)行環(huán)境指的是變量在執(zhí)行階段所在的作用域,執(zhí)行環(huán)境定義了變量或函數(shù)有權(quán)訪問的其他數(shù)據(jù),每一個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象,環(huán)境中所有的變量都保存在這個(gè)對象中;

var a = 1;
function fn (args){
    console.log(a+args)
}
fn(1)//2

上述代碼的存在兩個(gè)執(zhí)行環(huán)境,每個(gè)執(zhí)行環(huán)境都有與之關(guān)聯(lián)的變量對象,變量a和函數(shù)fn保存在全局變量對象window當(dāng)中,保存函數(shù)的參數(shù)的agruments對象保存在局部變量對象fn當(dāng)中;
值得注意的是,如果這個(gè)執(zhí)行環(huán)境是函數(shù),則將其活動(dòng)對象作為變量對象,即函數(shù)調(diào)用時(shí)所生成的對象,因?yàn)楹瘮?shù)未調(diào)用定義在里面的變量是不存在的;
1.2 作用域鏈
當(dāng)代碼在一個(gè)環(huán)境中執(zhí)行時(shí),會(huì)創(chuàng)建變量對象的一個(gè)作用域鏈,作用域的用途是能夠保證執(zhí)行環(huán)境有權(quán)、有序訪問當(dāng)前作用域及其外部的變量;

var a =1;
function fn(b,c){
    var d = 4;
    console.log(a+b+c+d)
}
fn(2,3)//10

以上述代碼為例,去探討執(zhí)行環(huán)境和作用域鏈的相關(guān)知識點(diǎn);

js引擎在解析階段將變量a和函數(shù)fn保存在window變量對象上,此時(shí)變量a和函數(shù)fn的執(zhí)行環(huán)境是window對象;

在調(diào)用函數(shù)fn時(shí),函數(shù)fn創(chuàng)建創(chuàng)建一個(gè)活動(dòng)對象fn(),函數(shù)內(nèi)部的變量b、c(保存在函數(shù)的arguments對象中)和變量d的執(zhí)行環(huán)境是活動(dòng)對象fn();

此時(shí),函數(shù)內(nèi)部的代碼在執(zhí)行過程中會(huì)創(chuàng)建活動(dòng)對象的作用域鏈,它可訪問到的變量處理有保存在arguments對象的b和c,直接定義在內(nèi)部的d,同時(shí)可以訪問到定義在外部執(zhí)行環(huán)境——window對象的a;

因此,這就是一條簡單的作用域鏈,同時(shí),這條作用域鏈?zhǔn)絾蜗颍ㄓ蓛?nèi)向外可訪問)、有序的;

1.3 關(guān)于JavaScript的塊級作用域
JavaScript是不存在塊級作用域的,只用函數(shù)才能多帶帶開辟一個(gè)作用域來;
上一段比較經(jīng)典代碼,來為塊級作用域和接下來的閉包預(yù)預(yù)熱;

//html
  • 1
  • 2
  • 3
  • 4
  • 5
//js var lists = document.querySelectorAll("li") console.log(lists) for(var i=0;i

【demo】

原本的需求是點(diǎn)擊第幾個(gè)li,控制臺彈出第幾個(gè)li的下標(biāo),但實(shí)際彈出的都是5;

出現(xiàn)這個(gè)問題的原因是,for這個(gè)流程控制語句是無法創(chuàng)建塊級作用域的,如果能夠創(chuàng)建塊級作用域的話,那么每循環(huán)一次,function里面的i都是保存當(dāng)時(shí)的i;

而實(shí)際上由于for不存在款及作用域,所以循環(huán)遍歷后,i=5,當(dāng)觸發(fā)點(diǎn)擊事件時(shí),調(diào)用函數(shù)console.log(i)就變成5;

如果想用實(shí)現(xiàn)原本的需求,那么我們就需要在每次循環(huán)時(shí)都能夠創(chuàng)建一個(gè)能夠保存當(dāng)時(shí)i的塊級作用域來;

具體如何實(shí)現(xiàn)需要用到接下來的知識點(diǎn),所以答案在后頭;

2. 立即執(zhí)行函數(shù)

所謂立即執(zhí)行函數(shù)(Immediately-Invoked Function Expression,IIFE),就是函數(shù)聲明后立即調(diào)用該函數(shù),具體的寫法為:

(function(){
  console.log(1)
})();
//1
可以傳參
(function(a){
  console.log(a)
})(5);
//5

初學(xué)者可能對立即執(zhí)行函數(shù)的寫法感到奇怪,其實(shí)只要了解js背后的解析原理就很容易掌握其規(guī)則;
立即執(zhí)行函數(shù)由一個(gè)匿名函數(shù)和兩個(gè)圓括號構(gòu)成,將匿名函數(shù)放在第一個(gè)圓括號內(nèi);

1.()();
2.function(){};
3.(function(){})()

之所以出現(xiàn)這樣的寫法,是js默認(rèn)出現(xiàn)在行首的函數(shù)解析為語句(聲明式)

//聲明式
function(){};
//表達(dá)式
var f = function(){}

如果語句后面直接出現(xiàn)圓括號會(huì)報(bào)錯(cuò),通過圓括號將語句括起來從而讓js識別為表達(dá)式,然后再加一個(gè)圓括號立即調(diào)用,從而實(shí)現(xiàn)一個(gè)立即執(zhí)行函數(shù);

function(){}();//報(bào)錯(cuò)
(function(){})()//不報(bào)錯(cuò)

立即執(zhí)行函數(shù)的優(yōu)點(diǎn)在于:

具有函數(shù)獨(dú)立開辟一個(gè)作用域的功能,實(shí)現(xiàn)私有變量封裝;

定義好后即可執(zhí)行內(nèi)部的代碼;

匿名特性不必?fù)?dān)心污染同級或上級的變量;

3. 閉包知識點(diǎn)
3.1什么是閉包

前面已經(jīng)講到執(zhí)行環(huán)境和作用域鏈,我們知道執(zhí)行環(huán)境定義了變量有權(quán)訪問其他數(shù)據(jù),函數(shù)能夠創(chuàng)建一個(gè)作用域。如果我們現(xiàn)在有這么一個(gè)需求:想要在外部環(huán)境下也能訪問內(nèi)部環(huán)境的變量,那么究竟如何實(shí)現(xiàn)呢?
這里接需要引出閉包的概念:閉包(closure)指的是能夠訪問另一個(gè)函數(shù)內(nèi)部變量的函數(shù);

function outer(){
    var a = 1;
    function inner(){
      return a
    }
    return inner
}
var result = outer();
result();//1

上述代碼中,變量a是在函數(shù)outer內(nèi)聲明,所以只有函數(shù)outer內(nèi)部才可訪問,外部全局環(huán)境無法訪問;

通過在outer內(nèi)部定義一個(gè)函數(shù)inner,因?yàn)樽饔糜蜴湹淖饔茫@個(gè)inner可以訪問變量a;

最后暴露一個(gè)inner接口,使得在調(diào)用outer時(shí),獲得inner這個(gè)接口,在調(diào)用這個(gè)接口,從而達(dá)到訪問函數(shù)內(nèi)部變量的目的;

通過上述分析,我們可以知道,函數(shù)inner就是閉包,即能夠訪問另一個(gè)函數(shù)內(nèi)部變量的函數(shù);

3.2 使用閉包的意義和注意點(diǎn)

使用閉包的意義
閉包的意義在于:實(shí)現(xiàn)函數(shù)的封裝,將變量全部封裝在函數(shù)內(nèi)部而不必?fù)?dān)心污染全局環(huán)境,只暴露出接口,而不必關(guān)心內(nèi)部的代碼邏輯,例如將對象的私有屬性和方法進(jìn)行封裝:

function Animal(name){
    var _age;
    function setAge(age){
      _age = age
    };
    function getAge(){
      return _age
    }  
    return {
      name:name,
      setAge:setAge,
      getAge:getAge
    }
}
var cat = Animal("cat");
cat.setAge(12)
cat.getAge()//12

上述代碼封裝了對象的私有屬性_age和私有方法setAge、getAge,暴露出一個(gè)對象作為接口;

函數(shù)Animal內(nèi)部定義的變量外部是直接無法訪問,只能通過對象接口間接訪問;

我們只需要調(diào)用對象相關(guān)的屬性和方法,而不必關(guān)心方法具體是如何實(shí)現(xiàn)的,也不必?fù)?dān)心內(nèi)部定義的變量會(huì)對全局變量有污染;
使用閉包的注意點(diǎn)

使用閉包會(huì)產(chǎn)生內(nèi)存泄露的問題。通常函數(shù)在調(diào)用完后,js內(nèi)部的垃圾回收機(jī)制會(huì)自動(dòng)銷毀局部活動(dòng)對象(函數(shù)調(diào)用時(shí)生成的對象),但是以上述代碼為例:

var cat = Animal("cat")這段代碼執(zhí)行完后,按道理會(huì)銷毀函數(shù)Animal這個(gè)活動(dòng)對象,實(shí)際上這個(gè)活動(dòng)對象仍存在內(nèi)存中;

原因是return {...}的這個(gè)對象的私有方法的作用域鏈仍在引用這個(gè)Animal活動(dòng)對象;

只有return {...}的這個(gè)對象被銷毀后,Animal活動(dòng)對象才能被銷毀,內(nèi)存才能釋放;
可通過添加以下代碼釋放內(nèi)存:

var cat = Animal("cat");
cat.setAge(12)
cat.getAge()//12
cat = null
3.3 閉包的具體應(yīng)用

回到前面的代碼中來:

//html
  • 1
  • 2
  • 3
  • 4
  • 5
//js var lists = document.querySelectorAll("li") console.log(lists) for(var i=0;i

實(shí)現(xiàn)點(diǎn)擊哪個(gè)list,就在控制臺輸出i;

通過立即執(zhí)行函數(shù)形成獨(dú)立的作用域;

傳遞參數(shù)進(jìn)IIFE,從而每循環(huán)一次就形成函數(shù)內(nèi)部的變量;

最后return出一個(gè)函數(shù),這個(gè)函數(shù)的返回值就是當(dāng)時(shí)的i;

var lists = document.querySelectorAll("li")
console.log(lists)
for(var i=0;i
【demo】
4.小結(jié)

通過整篇文章,我們知道了:

執(zhí)行環(huán)境指的是變量在執(zhí)行階段所在的作用域,定義了變量有權(quán)訪問的其他數(shù)據(jù),每個(gè)執(zhí)行環(huán)境都有一個(gè)與之關(guān)聯(lián)的變量對象,所有的變量都保存在其中,函數(shù)在執(zhí)行階段會(huì)創(chuàng)建一個(gè)活動(dòng)對象,這活動(dòng)對象可以包含以下變量:arguments,函數(shù)內(nèi)部使用var定義的變量和外部變量;

變量在執(zhí)行階段會(huì)創(chuàng)建變量對象的一個(gè)作用域鏈,作用域鏈的用途是保證執(zhí)行環(huán)境有權(quán)有序訪問當(dāng)前作用域和其他外部變量;

JavaScript不存在塊級作用域;

閉包是能夠訪問函數(shù)內(nèi)部變量的函數(shù),閉包的意義在于能夠封裝變量在函數(shù)內(nèi)部而實(shí)現(xiàn)間接訪問函數(shù)內(nèi)部變量;使用閉包要注意內(nèi)存泄露問題,解決辦法是在調(diào)用完接口后,可以使用賦值null進(jìn)行銷毀;

參考資料

《JavaScript高級程序設(shè)計(jì)(第3版)》

《JavaScript標(biāo)準(zhǔn)參考教程》——阮一峰

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

轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/86580.html

相關(guān)文章

  • 細(xì)說 Javascript 函數(shù)篇() : 閉包和引用

    Javascript 中一個(gè)最重要的特性就是閉包的使用。因?yàn)殚]包的使用,當(dāng)前作用域總可以訪問外部的作用域。因?yàn)?Javascript 沒有塊級作用域,只有函數(shù)作用域,所以閉包的使用與函數(shù)是緊密相關(guān)的。 模擬私有變量 function Counter(start) { var count = start; return { increment: function(...

    shevy 評論0 收藏0
  • 進(jìn)擊JavaScript之()玩轉(zhuǎn)閉包

    摘要:為了更好的理解,在閱讀此文之前建議先閱讀上一篇進(jìn)擊之詞法作用域與作用域鏈?zhǔn)裁词情]包閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。在中函數(shù)構(gòu)成閉包。 為了更好的理解,在閱讀此文之前建議先閱讀上一篇《進(jìn)擊JavaScript之詞法作用域與作用域鏈》 1.什么是閉包 閉包的含義就是閉合,包起來,簡單的來說,就是一個(gè)具有封閉功能與包裹功能的結(jié)構(gòu)。所謂的閉包就是...

    cyixlq 評論0 收藏0
  • Js基礎(chǔ)知識() - 作用域與閉包

    摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對應(yīng)的序號? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...

    lemanli 評論0 收藏0
  • Js基礎(chǔ)知識() - 作用域與閉包

    摘要:是詞法作用域工作模式。使用可以將變量綁定在所在的任意作用域中通常是內(nèi)部,也就是說為其聲明的變量隱式的劫持了所在的塊級作用域。 作用域與閉包 如何用js創(chuàng)建10個(gè)button標(biāo)簽,點(diǎn)擊每個(gè)按鈕時(shí)打印按鈕對應(yīng)的序號? 看到上述問題,如果你能看出來這個(gè)問題實(shí)質(zhì)上是考對作用域的理解,那么恭喜你,這篇文章你可以不用看了,說明你對作用域已經(jīng)理解的很透徹了,但是如果你看不出來這是一道考作用域的題目,...

    XFLY 評論0 收藏0

發(fā)表評論

0條評論

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