关于 js 闭包和异步的问题

2017-04-18 15:57:05 +08:00
 ma199385

第一种情况

for (var i = 0; i < 10; i++) {

  setTimeout(function(){
  
  console.log('i-'+i);
  
  return function(){
    console.log('i-'+i);
  }
 }(), 2000)
}

执行结果为 i-1,i-2...i-9 。两秒后输出 10 次 i-10

第二种情况 将代码改为如下

for (var i = 0; i < 10; i++) {

  setTimeout(function(){
    var s = i
  console.log('i-'+i);
  return function(){
    console.log('s-'+s);
  }
 }(), 2000)
}

执行结果为 i-1,i-2...i-9 。两秒后输出 s-1,s-2...s-9

第三种情况

for (var i = 0; i < 10; i++) {
    
  (function(){
  console.log('i-'+i);
  return function(){
    console.log('s-'+i);
  }()
 })()
}

执行结果为i-1,s-1...i-9,s-9 这是什么原因呢

2158 次点击
所在节点    程序员
8 条回复
learnshare
2017-04-18 16:01:30 +08:00
闭包保存了“现场”,也就是当时的状态
ma199385
2017-04-18 16:04:33 +08:00
@learnshare 您在看下我添加的第三种情况
jklopsdfw
2017-04-18 16:31:11 +08:00
@ma199385 第三种情况 return undefined ,然后两个立即执行函数,没毛病
ma199385
2017-04-18 16:34:19 +08:00
@jklopsdfw 第一种情况和第二种情况能否说明闭包只关联上一个外层函数的变量
kutata
2017-04-18 16:38:37 +08:00
其实我觉得这应该是作用域的问题,面非闭包。
第一种情况你把 var 改为 let 就输出就正常了。

具体可以参考以下这篇文章,<块级作用域> 章节。
http://lifemap.in/es2015-in-action/
jklopsdfw
2017-04-18 16:41:26 +08:00
@ma199385 第一种情况定时器调用的函数在两秒后只能找到两秒后外层的 i ,第二种情况定时器调用的函数两秒后找到两秒前赋值的 s ,第三种情况没有定时器,啥时候调用就找啥时候的 i
xss
2017-04-18 16:52:27 +08:00
你的 setTimeout 里面的回调函数, 存在于特定的上下文中.
顺着原型链网上找:
1. 首先 i-1,i-2...i-9 是第一个 console.log 输出的.然后 2 秒之后返回了函数,此时 for 循环已经执行完毕, i 的值在原型连上是 10, 所以再输出 10 次 10.

2. 第一次的输出同 1, 不同的是, 第二次输出的是 s 的值, 而每次原型链上 s 的值是 i 的副本(值传递), 所以第二次会输出 s-(保存的 i 值), s-(保存的 i 值). 但是注意, 实际上保存的 i 值的顺序并不一定是递减或递增的关系, 在极端情况下可能是乱序的. 这个和定时器的精度有关.

3. 函数调用栈问题, 最外侧匿名函数首先被调用, 然后内部的匿名函数再获得调用机会. 每次 for 循环都是如此, 先调用外部的函数, 输出 i-1, 然后内部匿名函数获得调用, 输出 s-1, 其余情况类推.
ma199385
2017-04-18 17:18:45 +08:00
@kutata
@jklopsdfw
@xss 万分感谢

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/355656

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX