今天突然看到群里有童鞋問這樣的一個問題
function fn(){
var arr=[];
for(var i=0;i<5;i++){
arr[i] = function(){
return i;
}
}
return arr;
}
var b = fn();
for(var i = 0;i<5;i++){
console.log(b[i]());
}
第一眼看過去,可能一些接觸js這門語言不太久的童鞋們會認為輸出的結果是0,1,2,3,4
但是執(zhí)行完這段代碼之后卻發(fā)現(xiàn)循環(huán)出來了五次都是5,這是為什么呢?
當然一部分童鞋一眼能看出正確結果,我也相信有的童鞋雖然能知道結果,但是思路并不是特別清晰,今天就來分析下到底為什么會這樣執(zhí)行
這里涉及到了作用域的問題
我們猜想的是每次循環(huán),函數(shù)內部都會捕獲當前i的值
for(var i=0;i<5;i++){
arr[i] = function(){
return i; //0,1,2,3,4
}
}
但是根據(jù)作用域的原理,雖然循環(huán)了五次,而且函數(shù)都是在各自的循環(huán)之中定義的,but這里又要說一個但是了,強調下重點它們都被封閉在函數(shù)fn這個函數(shù)作用域之中,因此實際上只有一個i,每次循環(huán)都會給i重新賦值
注意:當fn執(zhí)行的時候循環(huán)執(zhí)行完成,而此時的匿名函數(shù)并沒有執(zhí)行,此刻的i已經在循環(huán)完成之后變成了5,
當執(zhí)行這段代碼的時候
for(var i = 0;i<5;i++){
console.log(b[i]());
}
b[i]會依次調用存儲于arr數(shù)組中的匿名函數(shù),而此刻匿名函數(shù)才開始執(zhí)行,去獲取i的值,因為i都位于fn的函數(shù)作用域下,此刻已經變成了5,所以,自然也就看到了輸出五次5
所以此時的函數(shù)都引用的是同一個i,循環(huán)會誤導你,讓你錯誤的判斷
那么,怎么樣才能避免這種情況,其實改造方法也很簡單,我們利用IIFE,也就是立即執(zhí)行函數(shù),來形成單獨的作用域
function fn(){
var arr=[];
for(var i=0;i<5;i++){
(function(i){
arr[i]=function(){
return i;
}
})(i);
}
return arr;
}
var b = fn();
for(var i = 0;i<5;i++){
console.log(b[i]());
}
這樣,在行成獨立的作用域之后,我們就可以拿到當前循環(huán)的i,因為擁有了獨立的作用域,i的值不會再相互影響
當再次調用位于arr數(shù)組中的匿名函數(shù)時,就可以找到儲存于立即執(zhí)行函數(shù)作用域中的i,而此刻的i正式我們希望看到的,會輸出0,1,2,3,4
這是我的一部分理解,如果有理解偏差,歡迎各位童鞋們一起探討