V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
ma199385
V2EX  ›  程序员

关于 js 闭包和异步的问题

  •  1
     
  •   ma199385 · 2017-04-18 15:57:05 +08:00 · 2027 次点击
    这是一个创建于 2536 天前的主题,其中的信息可能已经有所发展或是发生改变。

    第一种情况

    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 这是什么原因呢

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

    具体可以参考以下这篇文章,<块级作用域> 章节。
    http://lifemap.in/es2015-in-action/
    jklopsdfw
        6
    jklopsdfw  
       2017-04-18 16:41:26 +08:00 via Android
    @ma199385 第一种情况定时器调用的函数在两秒后只能找到两秒后外层的 i ,第二种情况定时器调用的函数两秒后找到两秒前赋值的 s ,第三种情况没有定时器,啥时候调用就找啥时候的 i
    xss
        7
    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
        8
    ma199385  
    OP
       2017-04-18 17:18:45 +08:00
    @kutata
    @jklopsdfw
    @xss 万分感谢
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1580 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 50ms · UTC 16:56 · PVG 00:56 · LAX 09:56 · JFK 12:56
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.