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

資訊專欄INFORMATION COLUMN

談?wù)凧avaScript的詞法環(huán)境和閉包(一)

AlphaWatch / 2774人閱讀

摘要:換句話說,定義在閉包中的函數(shù)可以記憶它被創(chuàng)建時候的環(huán)境。詞法環(huán)境的概念定義摘自百科。一個詞法環(huán)境由一個環(huán)境記錄項和可能為空的外部詞法環(huán)境引用構(gòu)成。中使用詞法環(huán)境管理靜態(tài)作用域。

一個資深的同事在我出發(fā)去面試前告誡我,問JS知識點的時候千萬別主動提閉包,它就是一個坑?。】影?!啊!

閉包確實是js的難點和重點,其實也沒那么可怕,關(guān)鍵是機制的理解,可以和函數(shù)一起多帶帶拿出來說說,其實關(guān)于閉包的解釋很多文章都寫得比較詳細(xì)了,這篇文章就作為自己學(xué)習(xí)過程的記錄吧。

閉包的概念

首先明確一下閉包的概念:

MDN (Mozilla Develop Network) 上的對閉包的定義:

閉包是指能夠訪問自由變量的函數(shù) (變量在本地使用,但在閉包中定義)。換句話說,定義在閉包中的函數(shù)可以“記憶”它被創(chuàng)建時候的環(huán)境。

分析:

閉包由函數(shù)和與其相關(guān)的引用環(huán)境(詞法環(huán)境)的組合而成

閉包允許函數(shù)訪問其引用環(huán)境(詞法環(huán)境)中的變量(又稱自由變量)

廣義上來說,所有JS的函數(shù)都可以稱為閉包,因為JS函數(shù)在創(chuàng)建時保存了當(dāng)前的詞法環(huán)境

還是很拗口有木有,一臉懵逼的時候就應(yīng)該從基礎(chǔ)的概念開始找,所以我們來談?wù)勗~法環(huán)境。

詞法環(huán)境的概念

定義(摘自wiki百科)。

詞法環(huán)境是一個用于定義特定變量和函數(shù)標(biāo)識符在ECMAScript代碼的詞法嵌套結(jié)構(gòu)上關(guān)聯(lián)關(guān)系的規(guī)范類型。一個詞法環(huán)境由一個環(huán)境記錄項和可能為空的外部詞法環(huán)境引用構(gòu)成。

變量作用域

一般來說,在編程語言中都有變量作用域的概念,每個變量都有自己的生命周期和作用范圍。
作用域有兩種解析方式:

靜態(tài)作用域
又稱為詞法作用域,在編譯階就可以決定變量的引用,由程序定義的位置決定,和代碼執(zhí)行順序無關(guān),用嵌套的方式解析。

動態(tài)作用域
在程序運行時候,和代碼的執(zhí)行順序決定。用動態(tài)棧動態(tài)管理。

var x = 10;
function getX() {
    alert(x);
}
function foo() {
    var x = 20;
    getX();
}
foo();

在靜態(tài)作用域下:
全局作用域下有x, getX, foo三個變量,getXfoo都有自己的作用域。執(zhí)行foo函數(shù)的時候,getX()被執(zhí)行,但是getX的定義位置在全局作用域下的,取到的x是10,而不是20。

在動態(tài)作用域下:
運行這段代碼時,先把x=10、getX、foo按順序壓棧,然后執(zhí)行foo函數(shù),在函數(shù)中把x=20壓棧,然后執(zhí)行getX(),此時距離棧頂最近的x值為20,因此alert的值也是20。

JavaScript使用的變量作用域是靜態(tài)作用域。JS中作用域簡單分為兩部分:全局作用域和函數(shù)作用域。ES5中使用詞法環(huán)境管理靜態(tài)作用域。

詞法環(huán)境包含兩部分

環(huán)境記錄

形參

函數(shù)聲明

變量

其它...

對外部詞法環(huán)境的引用(outer)

環(huán)境記錄初始化

一段JS代碼執(zhí)行之前,會對環(huán)境記錄進(jìn)行初始化(聲明提前),即將函數(shù)的形參、函數(shù)聲明和變量先放入函數(shù)的環(huán)境記錄中,特別需要注意的是:

形參會在初始化的時候定義值,但是函數(shù)內(nèi)部定義的變量只聲明不定義(不賦值),這個需要用JS中的Hoisting機制來解釋,具體可以看這一篇文章:《理解 JavaScript(二):Scoping & Hoisting》。

以下面這段代碼為例,解析環(huán)境記錄初始化和代碼執(zhí)行的過程:

var x = 10;
function foo(y) {
    var z  = 30;
    function bar(q) {
        return x + y + z + q;
    }
    return bar;
}
var bar = foo(20);
bar(40);

step1:初始化全局環(huán)境

全局環(huán)境
環(huán)境記錄(record) foo:
x: undefined(聲明變量而非定義變量)
bar: undefined(聲明變量而非定義變量)
外部環(huán)境(outer) null

step2: 執(zhí)行x=10

全局環(huán)境
環(huán)境記錄(record) foo:
x: 10()
bar: undefined(聲明變量而非定義變量)
外部環(huán)境(outer) null

step3:執(zhí)行var bar = foo(20)語句之前,將foo函數(shù)的環(huán)境記錄初始化

foo 環(huán)境
環(huán)境記錄(record) y: 20(定義形參)
bar:
z: undefined(聲明變量而非定義變量)
外部環(huán)境(outer) 全局環(huán)境

step4:執(zhí)行var bar = foo(20)語句,變量bar接收foo函數(shù)中返回的bar函數(shù)

foo 環(huán)境
環(huán)境記錄(record) y: 20
bar:
z: 30(定義z)
外部環(huán)境(outer) 全局環(huán)境

step5:執(zhí)行bar函數(shù)之前,初始化bar的詞法環(huán)境

bar環(huán)境
環(huán)境記錄(record) q: 40(定義形參q)
外部環(huán)境(outer) foo環(huán)境

step6:在foo函數(shù)內(nèi)執(zhí)行bar函數(shù)

    x + y + z + q = 10 + 20 + 30 + 40 = 100 

其實說了那么多,也是想強調(diào)一點:形參的值在環(huán)境初始化的時候就賦值了!因此形參的作用之一就是保存外部變量的值!

一道閉包的面試題

查了一下關(guān)于閉包的面試題,用具體的例子說明閉包的應(yīng)用場景。
最常見的答案來自于《JavaScript高級程序設(shè)計(第3版)》p181:

例子:

function creacteFunctions() {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function () {
            return i;
        }
    }
    return result;
}

這個函數(shù)返回了長度為10的函數(shù)數(shù)組,假設(shè)我們調(diào)用函數(shù)數(shù)組的第3個函數(shù),在控制臺中輸入creacteFunctions()[2](),即執(zhí)行函數(shù)數(shù)組里面的第三個函數(shù),creacteFunctions()返回函數(shù)數(shù)組,[2]是取第三個函數(shù)的引用,最后一個()是執(zhí)行第三個函數(shù),返回結(jié)果卻并不是預(yù)期的2,而是10.

因此,為了能夠讓閉包的行為符合預(yù)期,需要創(chuàng)建一個匿名函數(shù):

function creacteFunctions() {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = function (num) {
            return function() {
                return num;
            }
        }(i);
    }
    return result;
}

此時在控制臺中輸入creacteFunctions()[2](),即執(zhí)行函數(shù)數(shù)組里面的第三個函數(shù),返回的就是預(yù)期中的2。
有了詞法環(huán)境的初始化過程,這里也就非常容易理解了。匿名函數(shù)的形參num保存了每次執(zhí)行的i的值。在function(num){...}(i)這個結(jié)構(gòu)中,i作為形參num的實際值執(zhí)行這個匿名函數(shù),因此每次循環(huán)中的num直接初始化為i的值。
為了更清楚的提取這部分結(jié)構(gòu),我們將匿名函數(shù)命名為helper:

var helper = function (num) {
    return function() {
        return num;
    }
}

用helper函數(shù)重寫第二段代碼:

var helper = function (num) {
    return function() {
        return num;
    }
}
function creacteFunctions() {
    var result = new Array();
    for (var i = 0; i < 10; i++) {
        result[i] = helper(i);
    }
    return result;
}

在控制臺中輸入creacteFunctions()[2](),輸出的也是預(yù)期中的2。

未完待續(xù)哦,閉包可以講的東西太多啦!

一句話總結(jié)

真正理解了作用域也就理解了閉包.

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

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

相關(guān)文章

  • 談?wù)?/em>eval另

    摘要:直到有一天關(guān)于閉包某一天,小伙伴們討論到說關(guān)于閉包變量的問題時,君指出,如果一個函數(shù)沒有引用到其所處閉包的變量,那這個變量所指向的空間將被釋放。對于的引擎而言,如,,,無論閉包中是否包含,它們都不會釋放掉那些再也引用不到的變量。 之前在祼看ECMA262-5,在說到eval的地方,死活看不明白為什么會有一節(jié)專門扯到Direct Call to Eval: A direct ca...

    ybak 評論0 收藏0
  • 還擔(dān)心面試官問閉包?

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

    tinyq 評論0 收藏0
  • JavaScript基礎(chǔ)系列---閉包及其應(yīng)用

    摘要:所以,有另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。所以本文中將以維基百科中的定義為準(zhǔn)即在計算機科學(xué)中,閉包,又稱詞法閉包或函數(shù)閉包,是引用了自由變量的函數(shù)。 閉包(closure)是JavaScript中一個神秘的概念,許多人都對它難以理解,我也一直處于似懂非懂的狀態(tài),前幾天深入了解了一下執(zhí)行環(huán)境以及作用域鏈,可戳查看詳情,而閉包與作用域及作用域鏈的關(guān)系密不可分,所...

    leoperfect 評論0 收藏0
  • 《你不知道javascript》筆記_作用域與閉包

    摘要:建筑的頂層代表全局作用域。實際的塊級作用域遠(yuǎn)不止如此塊級作用域函數(shù)作用域早期盛行的立即執(zhí)行函數(shù)就是為了形成塊級作用域,不污染全局。這便是閉包的特點吧經(jīng)典面試題下面的代碼輸出內(nèi)容答案個如何處理能夠輸出閉包方式方式下一篇你不知道的筆記 下一篇:《你不知道的javascript》筆記_this 寫在前面 這一系列的筆記是在《javascript高級程序設(shè)計》讀書筆記系列的升華版本,旨在將零碎...

    galaxy_robot 評論0 收藏0
  • javascript閉包

    摘要:閉包的定義閉包是函數(shù)和聲明該函數(shù)的詞法作用域的組合。上面的和都是閉包。然而在一個閉包內(nèi)對變量的修改,不會影響到另一個閉包中的變量。原因是賦值給的是閉包。由于循環(huán)在事件觸發(fā)之前早已執(zhí)行完畢,變量被三個閉包共享已經(jīng)變成了。 閉包的定義: 閉包是函數(shù)和聲明該函數(shù)的詞法作用域的組合。 先看如下例子: function makeFn(){ var name = Mirror; f...

    binaryTree 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<