立即执行函数对变量值的影响

A:

var key=[];
for(var i = 0;i<5;i++){
    (function(){
        var x = i;
        key[i] = function(){
            console.log(x)
        }
    })();
}

B:

var key=[];
for(var i = 0;i<5;i++){
    var x = i;
    key[i] = function(){
        console.log(x)
    }
}

请问上下两种情况,一种在for循环中加了个IIFE,一种没加,对于中间这个x = i有什么影响,对于情况B来说,x = i不能实时取到i的值么?

阅读 3.8k
4 个回答

这还是闭包的问题。你可以在这两段代码后面都加上这么一段看看会是什么结果——

for(i = 0; i < key.length; i++) {
    key[i]();
}

答案是,A段代码会分别打印出01234,而B段代码会打印出5个4


原因其实很简单,

  1. key[i]里面仅仅只是存着函数而没有运行,所以它并没有和代码片段中的循环同时运行。

  2. var声明的变量属于函数作用域,所以对于B段代码而言,无论你循环多少次,xi都只有1个,这也就意味着key[i]函数里面存着的,对x的引用都是同一个。

  3. 所以当key[i]的函数运行的时候,它们引用的都是同一个x = 4,那么当然打印出来的就是5个4了。

  4. 对于A段代码而言,每次循环迭代都创建了个新的函数作用域,而其作用域内部的x当然也就随之创建了5个。

  5. 这也就意味着,A段代码中key[i]函数中对于x的引用并不是同一个,而是每次迭代之中关于i的副本。所以,A段代码可以连续输出i的值。

  6. 它们之中的区别就在于,B段代码之中引用的x是同一个,而A段代码中并不是。

第一个,引用的x是每次立即执行函数里吗的xkey的每一个值对应的x都不是一个。
第二个,总共就一个x,在全局。可以把var x = i ;改成 let x = i;

执行空间不一样,不加IIFE,x=i是在一个空间里被执行,for循环下面的代码,也是在这个空间里。如果加上IIFE,x=i就是在另一个空间里被执行,和for循环下面的代码,不是一个执行空间。就好比,同样是x=i,不加IIFE是在地球上=,加上就跑到火星上=。
对于B来说,之所以不能‘实时’取到i的值,是因为它采用了实时的方式去取i的值,因为for循环之外的语句只能访问当前i的状态,而不能访问i的历史状态,因为地球上只有i的当前状态。而采用了IIFE之后,i的历史状态封装后被从火星带到了地球,在地球上存在的除了i之外,还有i的历史状态,然后你如果run的是这个历史状态的封装,自然就可以取到i的历史‘实时’值。

最后你可以在for下面加一段代码,运行下就可以看出两者的区别。

//...
for(var func of key) {
    console.log(func());
}
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题