用閉包模擬私有方法
var makeCounter = function() {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function() {
changeBy(1);
},
decrement: function() {
changeBy(-1);
},
value: function() {
return privateCounter;
}
}
};
var Counter1 = makeCounter();
var Counter2 = makeCounter();
console.log(Counter1.value()); /* logs 0 */
Counter1.increment();
Counter1.increment();
console.log(Counter1.value()); /* logs 2 */
Counter1.decrement();
console.log(Counter1.value()); /* logs 1 */
console.log(Counter2.value()); /* logs 0 */
請注意兩個計數器 counter1 和 counter2 是如何維護它們各自的獨立性的。每個閉包都是引用自己詞法作用域內的變量 privateCounter 拍嵌。
每次調用其中一個計數器時,通過改變這個變量的值碘勉,會改變這個閉包的詞法環(huán)境掷匠。然而在一個閉包內對變量的修改,不會影響到另外一個閉包中的變量珊拼。
在循環(huán)中創(chuàng)建閉包厘托,常見的錯誤
<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
數組 helpText 中定義了三個有用的提示信息友雳,每一個都關聯(lián)于對應的文檔中的input 的 ID。通過循環(huán)這三項定義铅匹,依次為相應input添加了一個 onfocus 事件處理函數押赊,以便顯示幫助信息。
運行這段代碼后包斑,您會發(fā)現它沒有達到想要的效果流礁。無論焦點在哪個input上,顯示的都是關于年齡的信息罗丰。
原因是賦值給 onfocus 的是閉包神帅。這些閉包是由他們的函數定義和在 setupHelp 作用域中捕獲的環(huán)境所組成的。這三個閉包在循環(huán)中被創(chuàng)建萌抵,但他們共享了同一個詞法作用域找御,在這個作用域中存在一個變量item。當onfocus的回調執(zhí)行時绍填,item.help的值被決定霎桅。由于循環(huán)在事件觸發(fā)之前早已執(zhí)行完畢,變量對象item(被三個閉包所共享)已經指向了helpText的最后一項讨永。
解決這個問題的一種方案是使用更多的閉包:特別是使用前面所述的函數工廠:
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function makeHelpCallback(help) {
return function() {
showHelp(help);
};
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
var item = helpText[i];
// 這里運用了在一個閉包內對變量的修改滔驶,不會影響到另外一個閉包中的變量。
document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
}
}
setupHelp();
避免使用過多的閉包卿闹,可以用let關鍵詞:
function showHelp(help) {
document.getElementById('help').innerHTML = help;
}
function setupHelp() {
var helpText = [
{'id': 'email', 'help': 'Your e-mail address'},
{'id': 'name', 'help': 'Your full name'},
{'id': 'age', 'help': 'Your age (you must be over 16)'}
];
for (var i = 0; i < helpText.length; i++) {
let item = helpText[i];
document.getElementById(item.id).onfocus = function() {
showHelp(item.help);
}
}
}
setupHelp();
這個例子使用let而不是var揭糕,因此每個閉包都綁定了塊作用域的變量萝快,這意味著不再需要額外的閉包。