閉包,看這篇就夠了!

閉包

首先借用阮老師對閉包(closure)的概念做出的定義:

closure.jpg

在《JavaScript高級程序設(shè)計(jì)(第3版)》中文版中[3],具體描述在第7章函數(shù)表達(dá)式第7.2節(jié)(頁碼為第178頁):閉包是指有權(quán)訪問另一個(gè)函數(shù)作用域中的變量的函數(shù)。創(chuàng)建閉包的常見方式,就是在一個(gè)函數(shù)內(nèi)部創(chuàng)建另一個(gè)函數(shù)。

ECMAScript中,閉包指的是:

  1. 從理論角度:所有的函數(shù)。因?yàn)樗鼈兌荚趧?chuàng)建的時(shí)候就將上層上下文的數(shù)據(jù)保存起來了。哪怕是簡單的全局變量也是如此,因?yàn)楹瘮?shù)中訪問全局變量就相當(dāng)于是在訪問自由變量,這個(gè)時(shí)候使用最外層的作用域。

  2. 從實(shí)踐角度:以下函數(shù)才算是閉包:

    1. 即使創(chuàng)建它的上下文已經(jīng)銷毀,它仍然存在(比如,內(nèi)部函數(shù)從父函數(shù)中返回)
    2. 在代碼中引用了自由變量

閉包是怎么保存數(shù)據(jù)作為緩存數(shù)據(jù)使用?

無論什么時(shí)候在函數(shù)中訪問一個(gè)變量時(shí),就會從作用域鏈中搜索具有相應(yīng)名字的變量。一般來講,當(dāng)函數(shù)執(zhí)行完畢后,局部活動對象就會被銷毀,內(nèi)存中僅保存全局作用域(全局執(zhí)行環(huán)境的變量對象)。但是閉包的情況又有所不同,閉包是一個(gè)函數(shù)捕獲它被定義時(shí)所在的環(huán)境,這個(gè)環(huán)境在該函數(shù)的引用被銷毀前都是存在的。

demo1:讀取上級作用域的活動變量

function foo() {
  let x = 10;
  // 閉包,捕獲`foo`的環(huán)境。
  // 當(dāng)foo被調(diào)用時(shí),創(chuàng)建foo的執(zhí)行環(huán)境,初始化變量對象(變量聲明和方法聲明以及參數(shù))
  //當(dāng)捕捉到bar這個(gè)函數(shù)聲明時(shí),會在函數(shù)內(nèi)部創(chuàng)建bar的[[scope]]=foo的活動變量+foo的[[scope]]
  function bar() {
    return x;
  }

  return bar;
}
let x = 20;
// 調(diào)用`foo`來返回`bar`閉包。
//當(dāng)執(zhí)行到這個(gè)地方的時(shí)候,相當(dāng)于定義了一個(gè)函數(shù)bar,bar的[[scope]]=foo的活動變量+foo的[[scope]]
// 只要全局作用域不銷毀,那么bar的[[scpoe]]就不會銷毀,因此形成閉包(調(diào)用的函數(shù)foo()執(zhí)行完畢后其執(zhí)行環(huán)境應(yīng)該銷毀的,但是由于此處的函數(shù)表達(dá)式而沒有銷毀foo的執(zhí)行環(huán)境即bar的[[scope]],從而形成閉包)
let bar = foo();
bar(); // 10,而不是20!

demo2:讀寫上級作用域的活動變量

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

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