概念
直接來(lái)看兩個(gè)函數(shù)的對(duì)比
function count1() {
var arr1 = [];
for (var i=1; i<=3; i++) {
arr1.push(i*i);
}
return arr1;
}
function count2() {
var arr2 = [];
for (var i=1; i<=3; i++) {
arr2.push(function () {
return i * i;
});
}
return arr2;
}
執(zhí)行 count1()
調(diào)用第一個(gè)函數(shù)加矛,很簡(jiǎn)單特咆。我們會(huì)得到 arr1 = [1, 4, 9]
那么第二個(gè)函數(shù)呢?它居然在 arr2.push()
中執(zhí)行了函數(shù)定義谒兄,也就是說(shuō)玖姑,當(dāng)我們調(diào)用 count2()
以后,arr2
中將不會(huì)是三個(gè)數(shù)字液肌,而是三個(gè)函數(shù)挟炬。
我們得繼續(xù)調(diào)用 arr2
中的函數(shù)才能獲得結(jié)果。
已知執(zhí)行 count2()
得到 arr2
嗦哆, 而 arr2
中都是函數(shù)方法谤祖,需要括號(hào)來(lái)執(zhí)行。
嘗試 count2()[0]()
老速,居然得到結(jié)果為 16 粥喜!
這是為什么呢?梳理一下函數(shù)執(zhí)行的過(guò)程橘券。
-
count2()
執(zhí)行時(shí)额湘,定義arr2
為空秕铛。 - 接著進(jìn)行
for
循環(huán)賦予arr2
三個(gè)函數(shù)。注意缩挑,就在這里發(fā)生了變化但两。由于這種閉包結(jié)構(gòu)的特殊性,i * i
并不會(huì)立即執(zhí)行供置,因?yàn)樗鈱舆€包裹了一個(gè)函數(shù)定義嘛谨湘,你得調(diào)用函數(shù)才能得到結(jié)果。 - 隨著 for 循環(huán)的運(yùn)行芥丧,
i * i
從1 * 1
變化成2 * 2
紧阔,再變化... - 等到
return arr2
的時(shí)候,i 已經(jīng)為 4 了续担,arr2
中已經(jīng)是三個(gè)返回4 * 4
的待執(zhí)行函數(shù)擅耽,當(dāng)你執(zhí)行以后,結(jié)果當(dāng)然都是 16 物遇。
這也就是說(shuō)乖仇,在閉包結(jié)構(gòu)下,引用這種變化量是很危險(xiǎn)的询兴。
如果一定要引用的話乃沙,需要傳入一個(gè)立即執(zhí)行的匿名函數(shù)。
function count() {
var arr = [];
for (var i=1; i<=3; i++) {
arr.push((function (n) {
return function () {
return n * n;
}
})(i));
}
return arr;
}
也就是這一塊
(function(n) {
return function() {
return n * n;
}
})(i);
匿名函數(shù)的語(yǔ)法是這樣的:(function (x) { return x * x }) (3);
作用域
當(dāng)利用了作用域以后诗舰,就可以在函數(shù)里封裝狀態(tài)了警儒。
function create_counter(initial) {
var x = initial || 0;
return {
inc: function () {
x += 1;
return x;
}
}
}
inc
可以引用它上一級(jí)的 x 變量,而你從外部不能訪問(wèn)到這個(gè)變量 x 眶根。
學(xué)習(xí)資料
https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/00143449934543461c9d5dfeeb848f5b72bd012e1113d15000
里面還有個(gè)很變態(tài)的推導(dǎo)蜀铲,我并沒(méi)有去推...
http://www.ruanyifeng.com/blog/2009/08/learning_javascript_closures.html
另一篇通俗易懂的文章。