闭包(五)----一个常见的循环

function foo(){
    var arr = [];
    for(var i = 0; i < 2; i++){
        arr[i] = function(){
            return i;
        }
    }
    return arr;
}
var bar = foo();
console.log(bar[0]());//2 

以上的代码运行结果是2,而不是预想的0。接下来我们用执行环境图示的方法,来看看到底是哪里出了问题。
在这里插入图片描述
执行流首先创建并进入全局执行环境,进行声明提升过程。执行流执行到第10行的时候,创建并进入foo()函数执行环境,并进行声明提升。然后执行第2行,将arr赋值为[]。然后执行第3行,给arr[0]和arr[1]都赋值为一个匿名函数。然后执行第8行,以arr的值为返回值退出函数。由于此时有闭包的存在,所以foo()执行环境并不会被销毁。
在这里插入图片描述
执行流进入全局执行环境,继续执行第10行,将函数的返回值arr赋值给bar
在这里插入图片描述
执行流执行第11行,访问bar的第0个元素并执行。此时,执行流创建并进入匿名函数执行环境,匿名函数中存在自由变量i,需要使用其作用域链匿名函数 -> foo()函数 -> 全局作用域进行查找,最终在foo()函数的作用域找到了i,然后在foo()函数的执行环境中找到了i的值2,于是给i赋值2
在这里插入图片描述
 执行流接着执行第5行,以i的值2作为返回值返回。同时销毁匿名函数的执行环境。执行流进入全局执行环境,接着执行第11行,调用内部对象console,并找到其方法log,将bar0的值2作为参数放入该方法中,最终在控制台显示2
 由此我们看出,犯错原因是在循环的过程中,并没有把函数的返回值赋值给数组元素,而仅仅是把函数赋值给了数组元素。这就使得在调用匿名函数时,通过作用域找到的执行环境中储存的变量的值已经不是循环时的瞬时索引值,而是循环执行完毕之后的索引值

IIFE

由此,可以利用IIFE传参和闭包来创建多个执行环境来保存循环时各个状态的索引值。因为函数传参是按值传递的,不同传参的函数被调用时,会创建不同的执行环境。

function foo(){
	var arr=[];
	for(var i=0;i<2;i++){
		arr[i]=(function fn(j){
			return funtion test(){
				return j;
			}
		})(i)
	}
}

在这里插入图片描述

块级作用域

使用IIFE还是较为复杂,使用块级作用域则更为方便
由于块作用域可以将索引值重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值,相当于为每一次索引值都创建一个执行环境。

function foo(){
	var arr=[];
	for(let i=0;i<2;i++){
		arr[i]=function(){
			return i;
		}
	}
	return arr;
}
var bar=foo();
console.log(bar[0]());//0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值