最近在工作中遇到一个让人头疼的问题,需要定义21个函数,函数的主体是一样的,功能也是一致的,仅仅是返回值不同。基本的结构是这样的
var arr = ["a","b","c","d"];
var funcArr = [];
for (var i = 0; i < 4; i++) {
funcArr[i] = function(){
return arr[i];
}
}
var result = funcArr[0]();
console.log(result); //undefined
表面上看,每个函数都应该基于自己在数组中的位置返回不同的值,但是实际上,每次返回的都是undefined。这是为什么呢?
纠结了很久没解决掉。过了大概一个月的时间,在《js高程》上看到了一个相似的例子。
function createFunctions(){
var result = new Array();
for(var i=0; i<10; i++){
result[i] = function(){
return i;
}
}
return return
}
这个函数返回一个函数数组。还有一点点闭包的意味,但是这不是重点,重点是书上这就话“闭包只能取得包含函数中任何变量的最后一个值”。作用链的这种机制,注定了在循环中定义函数会产生这种副作用。虽然在实际中我并没有用到闭包,但是原理是一样的,都是作用域联的问题。当createFunctions()函数返回时,i的值为10,此时每个函数都引用着保存变量i的同一个变量对象,所以每个函数内部i的值都是10。
换做我遇到的问题。函数在应用时,循环早已停止,当前的i值是4,函数返回值是arr[4],就是undefined。
解决方案是创建另一个匿名函数强制让闭包行为符合预期。应用到我的例子就是这样:
var arr = ["a","b","c","d"];
var funcArr = [];
for (var i = 0; i < 4; i++) {
funcArr[i] = function(num){
return function(){
return arr[num];
};
}(i)
}
var result = funcArr[0]();
console.log(result); //"a"
立即执行函数保证了每个函数中的i是当时循环到的i值,即使循环结束,i值在循环中已经以副本num的形式确定下来了。
//2018-03-29******
可以使用ES6的新的声明变量的方法解决这个问题,简单优雅,看代码:
var arr = ["a","b","c","d"];
var funcArr = [];
//使用let代替var
for (let i = 0; i < 4; i++) {
funcArr[i] = function(){
return arr[i];
}
}
var result = funcArr[0]();
console.log(result); //"a"
let声明的变量是块级作用域的。循环中不会共享一个i值。