欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

詳解JavaScript中的作用域鏈與閉包

 更新時(shí)間:2022年11月30日 10:18:51   作者:大眼睛圖圖  
這篇文章主要為大家詳細(xì)介紹一下JavaScript中的作用域鏈與閉包的使用,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)JavaScript有一定的幫助,需要的可以參考一下

作用域鏈

首先來(lái)看看這段代碼:

var a = '喜羊羊';
function A(){
    console.log(a);
    a = '美羊羊';
    function B(){
        console.log(a);
    }
    B();
}
A();

在這里毫無(wú)疑問(wèn)結(jié)果肯定是我們想到的先打印喜羊羊,再打印美羊羊。因?yàn)樽饔糜蜴溌?,如果?dāng)前層沒(méi)找到,那么就去當(dāng)前層的上一級(jí)找。

那么再看這道

function bar() {
    console.log(myName)
}
function foo() {
    var myName = "極客邦"
    bar()
}
var myName = "極客時(shí)間"
foo()

是不是感覺(jué)是打印極客邦?如果是的話,那么恭喜你,掉坑里了。(還不趕快爬起來(lái),補(bǔ)一補(bǔ)作用域鏈的知識(shí))。

為什么打印不是極客邦而是極客時(shí)間呢?

既然問(wèn)題出現(xiàn)在了對(duì)作用域鏈的理解上,那么就再回到作用域鏈的定義上吧。

其實(shí)在每個(gè)執(zhí)行上下文的變量環(huán)境中,都包含了一個(gè)外部引用,用來(lái)指向外部的執(zhí)行上下文,我們把這個(gè)外部引用稱為 outer。

比如上面那段代碼在查找 myName 變量時(shí),如果在當(dāng)前的變量環(huán)境中沒(méi)有查找到,那么 JavaScript 引擎會(huì)繼續(xù)在 outer 所指向的執(zhí)行上下文中查找

為了直觀理解,你可以看下面這張圖:

看到這張圖我猜你又納悶了,為什么bar函數(shù)創(chuàng)建的執(zhí)行上下文中的outer會(huì)指向全局??

哈哈哈,這里就要涉及到了詞法作用域了

詞法作用域

詞法作用域就是指作用域是由代碼中函數(shù)聲明的位置來(lái)決定的,所以詞法作用域是靜態(tài)的作用域,通過(guò)它就能夠預(yù)測(cè)代碼在執(zhí)行過(guò)程中如何查找標(biāo)識(shí)符。

這么講可能不太好理解,你可以看下面這張圖:

從圖中可以看出,詞法作用域就是根據(jù)代碼的位置來(lái)決定的,其中 main 函數(shù)包含了 bar 函數(shù),bar 函數(shù)中包含了 foo 函數(shù),因?yàn)?JavaScript 作用域鏈?zhǔn)怯稍~法作用域決定的,所以整個(gè)詞法作用域鏈的順序是:foo 函數(shù)作用域—>bar 函數(shù)作用域—>main 函數(shù)作用域—> 全局作用域。

明白了詞法作用域,那么我們?cè)倩氐絼倓偟膯?wèn)題。

為什么bar函數(shù)創(chuàng)建的執(zhí)行上下文中的outer會(huì)指向全局

這是因?yàn)楦鶕?jù)詞法作用域,而詞法作用域又是根據(jù)代碼的位置,而bar函數(shù)代碼的位置就是包裹在全局下,而喜羊羊那個(gè)例子中的B函數(shù)是在A函數(shù)的環(huán)境下,所以會(huì)造成它們的詞法作用域鏈不同,也就導(dǎo)致函數(shù)作用域鏈不同了。

所以我們才有那句話詞法作用域是代碼編譯階段就決定好的,和函數(shù)是怎么調(diào)用的沒(méi)有關(guān)系。

也就是只和代碼位置有關(guān),和函數(shù)直接如何調(diào)用沒(méi)關(guān)系

閉包

老生常談的問(wèn)題,這次再?gòu)囊粋€(gè)更深入的角度來(lái)理解一下。

看下面這段代碼:

function foo() {
    var myName = "極客時(shí)間"
    let test1 = 1
    const test2 = 2
    var innerBar = {
        getName:function(){
            console.log(test1)
            return myName
        },
        setName:function(newName){
            myName = newName
        }
    }
    return innerBar
}
var bar = foo()
bar.setName("極客邦")
bar.getName()
console.log(bar.getName())

這段代碼乍一看沒(méi)有什么問(wèn)題,但是這里有一個(gè)細(xì)節(jié)很多人會(huì)忽視。

在foo()執(zhí)行完將返回值給bar時(shí),這里foo函數(shù)會(huì)從調(diào)用棧中彈出,變量都會(huì)被回收。既然變量都被回收了,那么bar.setName()這些調(diào)用方法從何而來(lái)??

foo執(zhí)行完后的情況可以參考下圖:

從上圖可以看出,foo 函數(shù)執(zhí)行完成之后,其執(zhí)行上下文從棧頂彈出了,但是由于返回的 setNamegetName 方法中使用了 foo 函數(shù)內(nèi)部的變量 myNametest1,所以這兩個(gè)變量依然保存在內(nèi)存中。這像極了 setNamegetName 方法背的一個(gè)專屬背包,無(wú)論在哪里調(diào)用了 setName getName 方法,它們都會(huì)背著這個(gè)foo函數(shù)的專屬背包。

之所以是專屬背包,是因?yàn)槌?setNamegetName 函數(shù)之外,其他任何地方都是無(wú)法訪問(wèn)該背包的,我們就可以把這個(gè)背包稱為 foo 函數(shù)的閉包。

好了,現(xiàn)在我們終于可以給閉包一個(gè)正式的定義了。在 JavaScript 中,根據(jù)詞法作用域的規(guī)則,內(nèi)部函數(shù)總是可以訪問(wèn)其外部函數(shù)中聲明的變量,當(dāng)通過(guò)調(diào)用一個(gè)外部函數(shù)返回一個(gè)內(nèi)部函數(shù)后,即使該外部函數(shù)已經(jīng)執(zhí)行結(jié)束了,但是內(nèi)部函數(shù)引用外部函數(shù)的變量依然保存在內(nèi)存中,我們就把這些變量的集合稱為閉包 比如外部函數(shù)是 foo,那么這些變量的集合就稱為 foo 函數(shù)的閉包。

用一句話概括就是

能夠訪問(wèn)其他函數(shù)內(nèi)部變量的函數(shù),被稱為 閉包。

(我們理解可以這么理解,但是和面試官說(shuō)的當(dāng)然可以把這個(gè)例子說(shuō)一下,這直接上升到了一個(gè)理解什么是閉包的新高度了)

到此這篇關(guān)于詳解JavaScript中的作用域鏈與閉包的文章就介紹到這了,更多相關(guān)JavaScript作用域鏈 閉包內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論