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

javascript中的闭包会在执行(求值)的时候从当前的运行上下文解析变量吗?

  •  
  •   pythonee · 2013-07-13 15:32:58 +08:00 · 3090 次点击
    这是一个创建于 4142 天前的主题,其中的信息可能已经有所发展或是发生改变。
    我是在这里看到这段代码的
    http://msdn.microsoft.com/zh-cn/library/hh968324.aspx

    var dofun = [];

    for (var i = 0; i < 10; i++) {
    dofun[i] = function() {
    return i;
    }

    }

    for (var i = 0; i < 10; i++) {
    alert(dofun[i]());
    }

    会输出0到9,因为第二个for循环同样用了 i。但是按正常的理解,第一个for循环,变量 i 经过var的修饰,应该算局部变量了呀,不至于从第二个for循环的 i 中每次赋值啊,所以按理应该是全部输出10才对吧,所以我想问,因为闭包是延迟求值,返回闭包后,对它的求值,外界的同名变量会干扰吗?按我的理解是不会的,我觉得这里应该是有什么地方我遗漏了,所以请各位指点一下。
    19 条回复    1970-01-01 08:00:00 +08:00
    zztczcx
        1
    zztczcx  
       2013-07-13 15:56:51 +08:00
    因为这里只有一个作用域就是 全局作用域。在第一个for循环里,闭包的作用域是全局的域(也就是运行这个函数的域),第二个for仍然是在同一个域中。
    我看你上一个帖子,那个闭包的创建你用的就挺对的啊。
    bakac
        2
    bakac  
       2013-07-13 15:57:57 +08:00
    var i = 0;
    for (i = 0; i < 10; i ++){
    dofun[i] = function(){
    return i;
    }
    }
    console.log(i); // i = 10;
    for (i = 0; i < 10; i++){
    //i 0 -> 9
    console.log(dofun[i]());
    }
    因为公用了 都引用了i ,而是第2个for又把i的值覆盖
    pythonee
        3
    pythonee  
    OP
       2013-07-13 17:47:18 +08:00
    @zztczcx
    @bakac

    我认真读完那个帖子,我大概明白了,这个东西不仅涉及作用域,还有就是闭包的激活时机,也就是延迟求值的发生时间,而作用域的话,还是那些,先从最近的找,不过这里的重点是闭包的求值时机
    Golevka
        4
    Golevka  
       2013-07-14 01:23:59 +08:00
    @pythonee 所以说javascript中for从句的scoping是一个和C++/C#/Java程序员直觉很不一样的设计————你在for(var i..中定义的i出了for之后还是可用的, 也就是说你在for里面定义的那个i是全局的.
    pythonee
        6
    pythonee  
    OP
       2013-07-14 13:25:31 +08:00
    @Golevka

    嗯,但是这里更多的是激活的时机,也就是说第一个for不会记忆当时的 i ,因为还没有被激活,所以在它调用的时候,也就是激活(求值)的时候,从第二个 for 循环中找到了 i
    pythonee
        7
    pythonee  
    OP
       2013-07-14 13:26:04 +08:00
    @chemzqm
    嗯,我不懂得时候,还是会先去那里看看,实在迷惑了,才会发帖求助
    Golevka
        8
    Golevka  
       2013-07-14 14:16:18 +08:00   ❤️ 1
    @pythonee 人家ECMA-262 13.2明确规定的lexical scoping的东西怎么一下子变成求值时解析了? 你在global scope创建的function object, F.[[Scope]]自然引用的是全局作用域. 并且你也知道所有的inner function都共享同一个parent scope, 那么parent scope里变量的值变了自然会影响closure的求值结果.
    zztczcx
        9
    zztczcx  
       2013-07-14 14:23:40 +08:00
    @Golevka 这是因为js 没有块作用域
    heroicYang
        10
    heroicYang  
       2013-07-14 14:38:46 +08:00
    @Golevka 点赞,非常赞同!
    clowwindy
        11
    clowwindy  
       2013-07-14 14:41:43 +08:00
    JavaScript 是 function scoping + static scoping,变量名绑定哪个变量是编译时就确定的。
    undozen
        12
    undozen  
       2013-07-14 14:56:45 +08:00
    楼主不如看看我之前写过的文章?
    http://zh.undozen.com/2013/10
    switch
        13
    switch  
       2013-07-14 16:22:05 +08:00
    @Golevka +1
    学 javascript 还是应该去看下 ECMAScript。
    Golevka
        14
    Golevka  
       2013-07-14 17:27:56 +08:00
    我感觉LZ是被误导了. 刚才点开LZ贴出的链接, 于是我感觉微软的那个MVP碉堡了.

    "函数是不是值得来锁定一个变量,是看该变量在调用这个函数的时候,是不是能在上下文作用域中找到这个变量,如果无法在调用时找到这个变量,内部函数就会锁住它,否则就不会锁住,至少表面上是这样的。"

    我擦咧? 难道我计算一个函数的free variable时还要关心它所有的call site么? 还有和"创建"函数的时机相关的说明我都不知该从哪吐嘈了.
    pythonee
        15
    pythonee  
    OP
       2013-07-14 22:14:58 +08:00
    @Golevka
    那我不是深深被误导了,我靠,我也觉得有点毁三观的感觉

    @clowwindy
    你说的块作用域是指什么?我内部函数确实有作用域啊,我用var声明的变量也有啊


    @clowwindy
    什么叫编译期绑定,就拿这个例子来讲,第一个for循环 创建函数的时候,并没有绑定到每个 i,是运行期求出来的啊
    pythonee
        16
    pythonee  
    OP
       2013-07-14 22:31:07 +08:00
    @Golevka
    其实我觉得 那个mvp说的也没有问题的样子,它并不是说关心call site,是说在 ()或 return的时候会锁定变量,假如不是像他说的那样的话,按理来说,第一个for就会求值的话,那么这时候 闭包里的 i 都是10了,也就没有第二个for什么事了
    Golevka
        17
    Golevka  
       2013-07-14 23:42:24 +08:00   ❤️ 1
    @pythonee .................................

    ECMAScript中的closure"锁定"的不是变量的值, 而是environment; 并且"锁定"的时机也不是在()/return/求值时, 而是在"定义"时. 不信你翻一下262看看在遇到"FunctionDeclaration"这条产生式时编译器需要做什么动作? Entering Function Code时又要做什么动作? 顺便SICP的3.2 The Environment Model of Evaluation也可拿来作为参考, 因为那里描述的也是带upward的模型, 也即js-er所谓的scope chaining.
    pythonee
        18
    pythonee  
    OP
       2013-07-15 08:45:50 +08:00
    @Golevka
    哦,这下总算找到正统了,我之前还看了一篇 javascript执行上下文的文章,这里的环境应该就是指的上下文吧,上下文的建立应该是在定义时,赋值是在执行时。哈哈,非常感谢你的回答,我突然觉得javascript很多概念挺统一的啊,之前被它的变化多端弄得神魂颠倒
    Mutoo
        19
    Mutoo  
       2013-07-15 09:45:26 +08:00
    var dofun = [];
    var i = 0; // global i
    for (i = 0; i < 10; i++) {
    (function() {
    var that_i = i; // encloure_i
    dofun[i] = function() {
    return that_i;
    }
    })();
    }
    console.log(i); // i = 10;
    for (i = 0; i < 10; i++) {
    //i 0 -> 9
    console.log(dofun[i]());
    }
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1361 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 17:47 · PVG 01:47 · LAX 09:47 · JFK 12:47
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.