V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
推荐关注
Meteor
JSLint - a JavaScript code quality tool
jsFiddle
D3.js
WebStorm
推荐书目
JavaScript 权威指南第 5 版
Closure: The Definitive Guide
manyfreebug

如何解释这段 JavaScript 代码的输出结果?

  •  1
     
  •   manyfreebug · Jun 30, 2021 · 3854 views
    This topic created in 1769 days ago, the information mentioned may be changed or developed.
    23 replies    2021-07-01 18:45:20 +08:00
    pcslide
        1
    pcslide  
       Jun 30, 2021
    这里是个 array of function

    这里的 rule 只是一个入参的代号,并不是一个变量
    mxT52CRuqR6o5
        3
    mxT52CRuqR6o5  
       Jun 30, 2021 via Android   ❤️ 1
    又不是全局变量,为什么会被覆盖
    farmer001
        4
    farmer001  
       Jun 30, 2021   ❤️ 1
    因为每次函数执行都是不同的作用域(上下文?),所以并不会覆盖
    KyrieJoshua
        5
    KyrieJoshua  
       Jun 30, 2021   ❤️ 1
    内部函数可以访问外层函数的作用域里的变量,我是这么理解的;
    定义的时候匿名函数的 rule 就是访问的外部函数里的变量;而且外部函数每次执行的时候都会重新声明一次 rule,所以不会互相覆盖;可以在匿名函数里打断点来查看调用栈;
    如果使用 this.rule 来保存就会不一样了
    JerryCha
        6
    JerryCha  
       Jun 30, 2021   ❤️ 1
    似乎形成闭包了
    ThomasTrainset
        7
    ThomasTrainset  
       Jun 30, 2021 via iPhone
    基础不扎实
    KrisWuSkrSkr
        8
    KrisWuSkrSkr  
       Jun 30, 2021
    我的理解是闭包了,保存了当时的上下文 rule 。
    tinkerer
        9
    tinkerer  
       Jun 30, 2021
    2 楼正解。
    Rocketer
        10
    Rocketer  
       Jun 30, 2021 via iPhone
    你每次执行 add 方法都声明了一个新的 rule,所以他们各自引用的是不同的对象
    Biwood
        11
    Biwood  
       Jun 30, 2021 via Android   ❤️ 1
    没有立即执行函数和 return 操作很多人就不认识闭包了

    按照闭包的解释,每次执行.add 操作的时候,在其内部会形成**一个独立的词法环境**,在这个词法环境中新创建的匿名函数(被 push 那个)会记住对环境中变量的引用,因为你在函数里内部执行了 console.log(rule),用到了当时环境中的 rule,所以这个 rule 变量会跟函数绑在一起,形成闭包

    你第二次执行 add 操作的时候,形成的是新的运行时上下文,push 进去的函数也是新的,该函数捆绑的变量也是新的,也就是一个新的闭包,所以不会覆盖上一次用到的变量

    你可以在 Chrome 的调试工具里用 console.dir()把最后一步 forEach 中的 fn 打印出来,可以看到闭包里面具体引用了哪些内容
    muzuiget
        12
    muzuiget  
       Jul 1, 2021
    闭包,建议重新理解。
    myCupOfTea
        13
    myCupOfTea  
       Jul 1, 2021
    这不就是闭包吗
    meepo3927
        14
    meepo3927  
       Jul 1, 2021   ❤️ 1
    形成闭包了,匿名函数访问的变量 Rule 不会释放。

    每次执行 add 方法,都会存储一个新的 Rule 变量以及值,通常在方法结束之后,这个变量会被释放,

    但是有闭包的情况,不会释放。
    zhanlanhuizhang
        15
    zhanlanhuizhang  
       Jul 1, 2021
    基本知识,没学好。
    sandman511
        16
    sandman511  
       Jul 1, 2021
    我后端,光看个标题就知道是闭包问题了(狗头
    no1xsyzy
        17
    no1xsyzy  
       Jul 1, 2021
    我还在想有什么可解释的,竟然是在说作用域?

    请回炉从 SICP 重造
    cenbiq
        18
    cenbiq  
       Jul 1, 2021
    就是不会被覆盖,因为两次执行了 add 方法,var rule 发生了两次
    lizhenda
        19
    lizhenda  
       Jul 1, 2021
    闭包
    libook
        20
    libook  
       Jul 1, 2021   ❤️ 1
    function 其实有两种调用模式,一种是函数,另一种是对象方法;不同调用模式表现出不同的特性,最直接的区别就是 function 运行的上下文。

    add 是声明在 RuleSystem 类(或者说是 RuleSystem 构造函数的原型)上的,当 new 出一个 ruleSystem 对象的时候,调用 ruleSystem.add 就是调用对象方法的模式,它的上下文是 ruleSystem 对象本身,所能直接操作 ruleSystem 的 rules 数组。

    在 add 方法内部向 rules push 的 item 是个 funciton,这个 funciton 不在原型链上,调用的时候是普通的函数调用模式,所以它的上下文是它所在的作用域(跟写 C 语言差不多),用到 rule 的时候就会自然向外层一层一层搜索 rule 这个关键字,于是找到了 var rule='Rule '+rule 。

    当你每次调用 add 方法的时候,都会产生一个新的局部作用域,这个作用域里有个 rule 变量,你一共调用了 2 次 add 方法,所以创建了 2 个局部作用域,这两个作用域里的 rule 值不一样,一个是"Rule A",另一个是"Rule B";两次 push 也都是在上述两个作用域里分别执行的,push 到 rules 的 function 里的 rule 也都是引用各自作用域里离它最近的那个 rule 。

    这时候代码等价于:

    var rules=[];

    var add=function(rule){
    var rule='Rule '+rule
    rules.push(function(){
    console.log(rule)
    })
    }

    两者区别仅仅是 rules 放在对象里还是直接放在上层作用域里,其余都是完全等价的。

    如果你希望 rules 数组里的所有函数中的 console.log(rule)都输出最后产生的 rule 值,你应该把 add 方法内部的 rule 变量换成一个公共变量,就是每次调用 add 方法不会重新创建的那种,比如直接放在上层作用域里,或者用 this.rule 。
    way2create
        21
    way2create  
       Jul 1, 2021
    我倒不觉得反直觉 不就应该这样么
    yzqtdu
        22
    yzqtdu  
       Jul 1, 2021
    词法作用域+闭包
    winteq
        23
    winteq  
       Jul 1, 2021
    闭包 下一位
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   2506 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 104ms · UTC 04:33 · PVG 12:33 · LAX 21:33 · JFK 00:33
    ♥ Do have faith in what you're doing.