1. 程式人生 > 實用技巧 >javascript中閉包的理解

javascript中閉包的理解

閉包

閉包(closure)是 JavaScript 語言的一個難點,也是它的特色,很多高階應用都要依靠閉包實現。

理解閉包,首先必須理解變數作用域。前面提到,JavaScript 有兩種作用域:全域性作用域和函式作用域。函式內部可以直接讀取全域性變數。

var n = 999;

function f1() {
  console.log(n);
}
f1() // 999

上面程式碼中,函式f1可以讀取全域性變數n

但是,正常情況下,函式外部無法讀取函式內部宣告的變數。

function f1() {
  var n = 999;
}

console.log(n)
// Uncaught ReferenceError: n is not defined(

上面程式碼中,函式f1內部宣告的變數n,函式外是無法讀取的。

如果出於種種原因,需要得到函式內的區域性變數。正常情況下,這是辦不到的,只有通過變通方法才能實現。那就是在函式的內部,再定義一個函式。

function f1() {
  var n = 999;
  function f2() {
  console.log(n); // 999
  }
}

上面程式碼中,函式f2就在函式f1內部,這時f1內部的所有區域性變數,對f2都是可見的。但是反過來就不行,f2內部的區域性變數,對f1就是不可見的。這就是 JavaScript 語言特有的"鏈式作用域"結構(chain scope),子物件會一級一級地向上尋找所有父物件的變數。所以,父物件的所有變數,對子物件都是可見的,反之則不成立。

既然f2可以讀取f1的區域性變數,那麼只要把f2作為返回值,我們不就可以在f1外部讀取它的內部變量了嗎!

function f1() {
  var n = 999;
  function f2() {
    console.log(n);
  }
  return f2;
}

var result = f1();
result(); // 999

上面程式碼中,函式f1的返回值就是函式f2,由於f2可以讀取f1的內部變數,所以就可以在外部獲得f1的內部變量了。

閉包就是函式f2,即能夠讀取其他函式內部變數的函式。由於在 JavaScript 語言中,只有函式內部的子函式才能讀取內部變數,因此可以把閉包簡單理解成“定義在一個函式內部的函式”。閉包最大的特點,就是它可以“記住”誕生的環境,比如f2

記住了它誕生的環境f1,所以從f2可以得到f1的內部變數。在本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。

閉包的最大用處有兩個,一個是可以讀取函式內部的變數,另一個就是讓這些變數始終保持在記憶體中,即閉包可以使得它誕生環境一直存在。請看下面的例子,閉包使得內部變數記住上一次呼叫時的運算結果。

function createIncrementor(start) {
  return function () {
    return start++;
  };
}

var inc = createIncrementor(5);

inc() // 5
inc() // 6
inc() // 7

上面程式碼中,start是函式createIncrementor的內部變數。通過閉包,start的狀態被保留了,每一次呼叫都是在上一次呼叫的基礎上進行計算。從中可以看到,閉包inc使得函式createIncrementor的內部環境,一直存在。所以,閉包可以看作是函式內部作用域的一個介面。

為什麼會這樣呢?原因就在於inc始終在記憶體中,而inc的存在依賴於createIncrementor,因此也始終在記憶體中,不會在呼叫結束後,被垃圾回收機制回收。

閉包的另一個用處,是封裝物件的私有屬性和私有方法。

function Person(name) {
  var _age;
  function setAge(n) {
    _age = n;
  }
  function getAge() {
    return _age;
  }

  return {
    name: name,
    getAge: getAge,
    setAge: setAge
  };
}

var p1 = Person('張三');
p1.setAge(25);
p1.getAge() // 25

上面程式碼中,函式Person的內部變數_age,通過閉包getAgesetAge,變成了返回物件p1的私有變數。

注意,外層函式每次執行,都會生成一個新的閉包,而這個閉包又會保留外層函式的內部變數,所以記憶體消耗很大。因此不能濫用閉包,否則會造成網頁的效能問題。

參考連結

物件

https://wangdoc.com/javascript/types/function.html#定義