閉包的一點(diǎn)理解

閉包

閉包就是可以讓函數(shù)訪問外部變量的函數(shù),其本質(zhì)就是一個函數(shù),mdn說“函數(shù)與對其狀態(tài)即詞法環(huán)境(lexical environment)的引用共同構(gòu)成閉包(closure)。也就是說,閉包可以讓你從內(nèi)部函數(shù)訪問外部函數(shù)作用域。在JavaScript,函數(shù)在每次創(chuàng)建時生成閉包。”
用mdn上面的例子來講

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}

let myFunc = makeFunc();
myFunc();

最后運(yùn)行發(fā)現(xiàn)兩個都打出了aaa,也就是說閉包的作用域鏈包含著它自己的作用域,以及包含它的函數(shù)的作用域和全局作用域,而且內(nèi)部函數(shù) displayName() 在執(zhí)行前,就已經(jīng)被外部函數(shù)返回。原因是:
JavaScript中的函數(shù)會形成閉包。 閉包是由函數(shù)以及創(chuàng)建該函數(shù)的環(huán)境組合而成。這個環(huán)境包含了這個閉包創(chuàng)建時所能訪問的所有局部變量。在我們的例子中,myFunc 是執(zhí)行 makeFunc 時創(chuàng)建的 displayName 函數(shù)實(shí)例的引用,而 displayName 實(shí)例仍可訪問其作用域中的變量,即可以訪問到 name 。由此,當(dāng) myFunc 被調(diào)用時,name 仍可被訪問,其值 Mozilla 就被傳遞到alert中。

注意?。。。?/h2>

作用域會被保留

通常,函數(shù)的作用域及其所有變量都會在函數(shù)執(zhí)行結(jié)束后被銷毀。但是,在創(chuàng)建了一個閉包以后,這個函數(shù)的作用域就會一直保存到閉包不存在為止。
eg

function Add(x) {
  return function(y) {
    return x + y;
  };
}
let add5 = Add(5);
let add10 = Add(10);

console.log(Add5(2));  // 7
console.log(Add10(2)); // 12

let add5=null
let add10=null//此時才可以被銷毀

從上述代碼可以看到add5 和 add10 都是閉包。它們共享相同的函數(shù)定義,但是保存了不同的環(huán)境。在 add5 的環(huán)境中,x 為 5。而在 add10 中,x 則為 10。這時候可以發(fā)現(xiàn)里面的那個關(guān)于y的函數(shù)和其作用域有被保留,直到最后通過 null才 釋放了 add5 和 add10 對閉包的引用。
在javascript中,如果一個對象不再被引用,那么這個對象就會被垃圾回收機(jī)制回收;
如果兩個對象互相引用,而不再被第3者所引用,那么這兩個互相引用的對象也會被回收。

閉包只能取得包含函數(shù)中的任何變量的最后一個值

eg.

function Arr(){
let arr=[]
for(i=0;i<10;i++){
arr[i]=function(){
return i}
}
return arr
}

這里,我們發(fā)現(xiàn)
沒return i

也就是說用閉包時候,只能return最后最外面那個值,當(dāng)Arr執(zhí)行完畢后,其作用域被銷毀,但它的變量對象仍保存在內(nèi)存中,得以被匿名訪問,這時i的值為10。
要想保存在循環(huán)過程中每一個i的值,需要在匿名函數(shù)外部再套用一個匿名函數(shù),在這個匿名函數(shù)中定義另一個變量并且立即執(zhí)行來保存i的值。

function Arr(){
let arr=[]
for(i=0;i<10;i++){
arr[i]=function(num){
return function(){
return num
}
}(i)
}
return arr
}

這時最內(nèi)部的匿名函數(shù)訪問的是num的值,所以數(shù)組中10個匿名函數(shù)的返回值就是1-10

閉包中的this對象

要注意this指向的是函數(shù)作用時候的環(huán)境,和函數(shù)名的環(huán)境要分開看。eg

var name=`window`
var obj={
name:`obj`,
getName:()=>{
return ()=>{
return this.name
}
}
}
console.log(obj.getName()())

在上面這段代碼中,obj.getName()()實(shí)際上是在全局作用域中調(diào)用了匿名函數(shù),this指向了window。


結(jié)果是window而不是obj

作用

閉包很有用,因?yàn)樗试S將函數(shù)與其所操作的某些數(shù)據(jù)(環(huán)境)關(guān)聯(lián)起來。這顯然類似于面向?qū)ο缶幊?。在面向?qū)ο缶幊讨?,對象允許我們將某些數(shù)據(jù)(對象的屬性)與一個或者多個方法相關(guān)聯(lián)。
因此,通常你使用只有一個方法的對象的地方,都可以使用閉包。
而且其最最最直觀的作用,就是我們可以通過這個方法,把我們的操作封裝起來,只留一些外部接口,這樣即可以使得后面的代碼看起來很簡潔,又可以不讓別人改動到我們的核心代碼。這個被稱為‘方法的私有化’。

缺點(diǎn)

閉包的缺點(diǎn):
比普通函數(shù)占用更多的內(nèi)存。
解決:閉包不在使用時,要及時釋放。
將引用內(nèi)層函數(shù)對象的變量賦值為null。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容