1. 程式人生 > >嵌套函數和閉包

嵌套函數和閉包

ever 變量 clas 就是 既然 不同 設置 解釋 同名

你可以在一個函數裏面嵌套另外一個函數。嵌套(內部)函數對其容器(外部)函數是私有的。它自身也形成了一個閉包。一個閉包是一個可以自己擁有獨立的環境與變量的的表達式(通常是函數)。

既然嵌套函數是一個閉包,就意味著一個嵌套函數可以”繼承“容器函數的參數和變量。換句話說,內部函數包含外部函數的作用域。

可以總結如下:

  • 內部函數只可以在外部函數中訪問。
  • 內部函數形成了一個閉包:它可以訪問外部函數的參數和變量,但是外部函數卻不能使用它的參數和變量。

下面的例子展示了嵌套函數:

function addSquares(a, b) {
  function square(x) {
    return
x * x; } return square(a) + square(b); } a = addSquares(2, 3); // returns 13 b = addSquares(3, 4); // returns 25 c = addSquares(4, 5); // returns 41

由於內部函數形成了閉包,因此你可以調用外部函數並為外部函數和內部函數指定參數:

function outside(x) {
  function inside(y) {
    return x + y;
  }
  return inside;
}
fn_inside = outside(3); //
Think of it like: give me a function that adds 3 to whatever you give it result = fn_inside(5); // returns 8 result1 = outside(3)(5); // returns 8

保存變量

註意到上例中 inside 被返回時 x 是怎麽被保留下來的。一個閉包必須保存它可見作用域中所有參數和變量。因為每一次調用傳入的參數都可能不同,每一次對外部函數的調用實際上重新創建了一遍這個閉包。只有當返回的 inside 沒有再被引用時,內存才會被釋放。

這與在其他對象中存儲引用沒什麽不同,但是通常不太明顯,因為並不能直接設置引用,也不能檢查它們。

多層嵌套函數

函數可以被多層嵌套。例如,函數A可以包含函數B,函數B可以再包含函數C。B和C都形成了閉包,所以B可以訪問A,C可以訪問B和A。因此,閉包可以包含多個作用域;他們遞歸式的包含了所有包含它的函數作用域。這個稱之為作用域鏈。(稍後會詳細解釋)

思考一下下面的例子:

function A(x) {
  function B(y) {
    function C(z) {
      console.log(x + y + z);
    }
    C(3);
  }
  B(2);
}
A(1); // logs 6 (1 + 2 + 3)

在這個例子裏面,C可以訪問B的y和A的x。這是因為:

  1. B形成了一個包含A的閉包,B可以訪問A的參數和變量
  2. C形成了一個包含B的閉包
  3. B包含A,所以C也包含A,C可以訪問B和A的參數和變量。換言之,C用這個順序鏈接了B和A的作用域

反過來卻不是這樣。A不能訪問C,因為A看不到B中的參數和變量,C是B中的一個變量,所以C是B私有的。

命名沖突

當同一個閉包作用域下兩個參數或者變量同名時,就會產生命名沖突。更近的作用域有更高的優先權,所以最近的優先級最高,最遠的優先級最低。這就是作用域鏈。鏈的第一個元素就是最裏面的作用域,最後一個元素便是最外層的作用域。

看以下的例子:

function outside() {
  var x = 5;
  function inside(x) {
    return x * 2;
  }
  return inside;
}

outside()(10); // returns 20 instead of 10

命名沖突發生在return x上,inside的參數xoutside變量x發生了沖突。這裏的作用鏈域是{inside, outside, 全局對象}。因此insidex具有最高優先權,返回了20(insidex)而不是10(outsidex)。

嵌套函數和閉包