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,通過閉包getAge和setAge,變成了返回對象p1的私有變量。

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

摘自 wangdoc.com,原文地址:https://wangdoc.com/javascript/types/function.html#閉包

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章