前提概要
最近复习闭包,出现了一个for循环的案例,如下可见,点击事件触发,输出的都是3,而不是我们想要对应输出的0,1,2。于是便开始纠结为什么for循环里绑定函数输出的是都是最后的边界值。
function fn(){
for(var i=0; i<3; i++){
eleNode[i].onclick=function(){
console.log(i);//全为3
}
}
}
fn();
问题解析
1.首先观察一下什么时候for循环才会出现那样的结果呢,根据下面的代码可以知道,当for循环里有函数的时候,可能会出现这样子的情况。
//平常使用
for(var i = 0; i < 3; i++) {
console.log(i)//会依次输出0,1,2
}
//包含函数
var arr = [];
for(var i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
}
}
arr[0]();//输出的不是0,而是3
for(var i = 0; i < 3; i++) {
setTimeout(()=>{
console.log(i);//输出的都是3
},0);
}
for(var i=0; i<3; i++){
eleNode[i].onclick=function(){
console.log(i);//全为3
}
}
2.之所以会如此,是因为js里只有全局作用域和函数作用域,没有块级作用域,所以当for循环里的函数执行时,自身没有i变量,便会沿着作用域链寻找变量i,于是会去找到全局作用域,此时,全局作用域里的i为边界值,所以输出的值都为for循环里i的边界值。
//验证一下寻找的是全局作用域里的i,额外赋值i为100,输出的值便全为100
var arr = [];
for(var i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
}
}
var i = 100;
arr[0]();//输出的是100
解决方法
利用块级作用域
1.es6的let和const关键字可以创建块级作用域,将变量的关键字改为let,形成块级作用域,每一个函数其父作用域对应的应该是for循环形成的块级作用域(这个地方有点不确定是不是称之为父作用域....)里的i分别对应的是0,1,2。于是输出i分别是0,1,2
var arr = [];
for(let i = 0; i < 3; i++) {
arr[i] = function() {
console.log(i);
}
}
arr[0]();//输出0
arr[1]();//输出1
arr[2]();//输出2
利用函数作用域
2.使用立即执行函数,此时目标函数的父作用域为立即执行函数,其输出的值同样是追溯父作用域的。
var arr = [];
for(var i = 0; i < 3; i++) {
(function(arg){
arr[i] = function() {
console.log(arg);
}
})(i)
}
//立即执行函数部分可以这样子理解
var fn = function(arg){
arr[i] = function() {
console.log(arg)
}
}
fn(i);
参考链接: