什麼是閉包
JavaScript
函數是將要執行的代碼及執行這些代碼的作用域構成的一個綜合體。計算機術語稱這種代碼和作用域的綜合體爲閉包。故所有JavaScript
函數都是閉包。
但我們常說的JavaScript
閉包是指,一個嵌套函數被導出到它所定義的作用域外時,才明確地稱爲閉包。JavaScript閉包
閉包是
JavaScript
一個非常重要的特性,這意味着當前作用域總是能夠訪問外部作用域中的變量。 因爲函數是JavaScript
中唯一擁有自身作用域的結構,因此閉包的創建依賴於函數。
簡單寫法
該函數的私有持久變量,可以被多個函數共享
var uniqueID = (function() {
// 私有持久值
var id = 0;
return function() {
return id++;
}
})();
循環中的閉包
一個常見的錯誤出現在循環中使用閉包,開發人員在循環語句裏創建函數(內部計數)時經常得不到預期的結果,假設我們需要在每次循環中調用循環序號
for(var i = 0; i < 10; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
所輸入的內容不是
0-9
,取而代之的是打印10次10
。關鍵原因,在調用
console.log(i)
時,循環已經結束,同一個上下文中創建的閉包是共用一個[[Scope]]屬性,導致i已經被修改成了10
。在ECMAScript中,同一個父上下文中創建的閉包是共用一個
[[Scope]]
屬性的。也就是說,某個閉包對其中[[Scope]]的變量做修改會影響到其他閉包對其變量的讀取。解決方式
避免引用錯誤,獲取正確序號。我們需要引入自執行函數,包裹一下。傳入就是
i
的拷貝,這樣就能獲取正確的輸出。
for(var i = 0; i < 10; i++) {
(function(e) {
setTimeout(function() {
console.log(e);
}, 1000);
})(i);
}
將
setTimeout
包裹在一個匿名函數中,匿名函數擁有變量e
的引用,便不用被循環改變了。使用閉包的斷點
在此貼上《JavaScript權威指南》中,使用
閉包的斷點
代碼。由Steve Yen
所寫,用來捕獲一個函數中的當前作用域(包括局部變量和函數的參數),並返回其結果。
function inspect(inspector, title) {
var expression, result;
if("ignore" in arguments.callee) {
return;
}
while(true) {
var message = "";
if(title) {
message = title + "\n";
}
if(expression) {
message += "\n" + expression + " ==> " + result + "\n";
} else {
expression = "";
}
expression += "Enter an expression to evaluate:";
expression = prompt(message, expression);
if(!expression) {
return;
}
result = inspector(expression);
}
}
用斷點技術計算階乘的函數
function factorial(n) {
var inspector = function($) {
return eval($);
}
// inspect.ignore = true;
inspect(inspector, "Entering factorial()");
var result = 1;
while(n > 1) {
result *= n;
n--;
inspect(inspector, "factorial() loop");
}
inspect(inspector, "Exiting factorial()");
return result;
}
參考