js作用域之for循环问题

159 阅读2分钟

前提概要

最近复习闭包,出现了一个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,额外赋值i100,输出的值便全为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);

参考链接:

blog.csdn.net/u013084331/…

juejin.cn/post/684490…

blog.csdn.net/kai_l/artic…