建議參考博客:http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
閉包是函數(shù)和聲明該函數(shù)的詞法環(huán)境的組合。
這個(gè)環(huán)境包含了這個(gè)閉包創(chuàng)建時(shí)所能訪問(wèn)的所有局部變量笆包。
先看下使用場(chǎng)景环揽,就能懂閉包到底是什么,閉包能用來(lái)干嘛庵佣。
1.在函數(shù)外使用函數(shù)內(nèi)的變量
function init(){
var name = "hello world";//name是一個(gè)被init創(chuàng)建的局部變量
function sayName(){//sayName是一個(gè)內(nèi)部函數(shù)歉胶,閉包
alert(name);//使用了父級(jí)函數(shù)聲明的變量name
}
sayName();
}
init();//"hello world"
sayName的alert語(yǔ)句彈出了“hello world”,成功的顯示了其父函數(shù)中聲明的name變量的值。
嵌套的函數(shù)可以訪問(wèn)其外部聲明的變量
2.將函數(shù)與其所操作的某些數(shù)據(jù)關(guān)聯(lián)起來(lái)巴粪,通常通今,你使用只有一個(gè)方法的對(duì)象的地方,都可以使用閉包
在 Web 中肛根,你想要這樣做的情況特別常見辫塌。大部分我們所寫的 JavaScript 代碼都是基于事件的 — 定義某種行為,然后將其添加到用戶觸發(fā)的事件之上(比如點(diǎn)擊或者按鍵)晶通。我們的代碼通常作為回調(diào):為響應(yīng)事件而執(zhí)行的函數(shù)璃氢。
<!DOCTYPE html>
<html>
<head>
<title></title>
</head>
<style type="text/css">
body {
font-family: Helvetica, Arial, sans-serif;
font-size: 12px;
}
h1 {
font-size: 1.5em;
}
h2 {
font-size: 1.2em;
}
</style>
<body>
<h1>hello world</h1>
<h2>hello world</h2>
<button id="a">12</button>
<button id="b">18</button>
<button id="c">22</button>
<script type="text/javascript">
document.getElementById("a").onclick = setSize(12);
document.getElementById("b").onclick = setSize(18);
document.getElementById("c").onclick = setSize(22);
function setSize(fontSize){
return function(){
document.body.style.fontSize = fontSize + 'px';
}
}
</script>
</body>
</html>
3.用閉包模擬私有方法
編程語(yǔ)言中,比如 Java狮辽,是支持將方法聲明為私有的一也,即它們只能被同一個(gè)類中的其它方法所調(diào)用。
而 JavaScript 沒(méi)有這種原生支持喉脖,但我們可以使用閉包來(lái)模擬私有方法椰苟。私有方法不僅僅有利于限制對(duì)代碼的訪問(wèn):還提供了管理全局命名空間的強(qiáng)大能力瑞躺,避免非核心的方法弄亂了代碼的公共接口部分玲销。
下面的示例展現(xiàn)了如何使用閉包來(lái)定義公共函數(shù),并令其可以訪問(wèn)私有函數(shù)和變量幢泼。
在之前的示例中题诵,每個(gè)閉包都有它自己的詞法環(huán)境洁仗;而這次我們只創(chuàng)建了一個(gè)詞法環(huán)境,為三個(gè)函數(shù)所共享:Counter.increment性锭,Counter.decrement 和 Counter.value赠潦。
該共享環(huán)境創(chuàng)建于一個(gè)立即執(zhí)行的匿名函數(shù)體內(nèi)。這個(gè)環(huán)境中包含兩個(gè)私有項(xiàng):名為 privateCounter 的變量和名為 changeBy 的函數(shù)草冈。這兩項(xiàng)都無(wú)法在這個(gè)匿名函數(shù)外部直接訪問(wèn)她奥。必須通過(guò)匿名函數(shù)返回的三個(gè)公共函數(shù)訪問(wèn)瓮增。
這三個(gè)公共函數(shù)是共享同一個(gè)環(huán)境的閉包。多虧 JavaScript 的詞法作用域哩俭,它們都可以訪問(wèn) privateCounter 變量和 changeBy 函數(shù)绷跑。
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();
Counter1.increment();
console.log(Counter1.value());//1
console.log(Counter2.value());//0
請(qǐng)注意兩個(gè)計(jì)數(shù)器 counter1 和 counter2 是如何維護(hù)它們各自的獨(dú)立性的。每個(gè)閉包都是引用自己詞法作用域內(nèi)的變量 privateCounter 凡资。
每次調(diào)用其中一個(gè)計(jì)數(shù)器時(shí)砸捏,通過(guò)改變這個(gè)變量的值,會(huì)改變這個(gè)閉包的詞法環(huán)境讳苦。然而在一個(gè)閉包內(nèi)對(duì)變量的修改带膜,不會(huì)影響到另外一個(gè)閉包中的變量。
以這種方式使用閉包鸳谜,提供了許多與面向?qū)ο缶幊滔嚓P(guān)的好處 —— 特別是數(shù)據(jù)隱藏和封裝。
4.循環(huán)里的閉包
怎么才能實(shí)現(xiàn)輸出0-5呢
for (var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000 * i);
}//55555
//方法一式廷,makeCallback函數(shù)為每一個(gè)回調(diào)創(chuàng)建一個(gè)新的詞法環(huán)境咐扭。
function makeCallback(i) {
return function() {
console.log(i)
};
}
for(var i=0;i<10;i++){
setTimeout(makeCallback(i),1000)
}
//另一種方法使用了匿名閉包
for(var i=0;i<10;i++){
(function(i){
setTimeout(function () {
console.log(i)
},1000)
})(i)
}
//使用let聲明變量
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
}, 1000 * i);
}