JavaScript本地作用域:var与this

JavaScript本地作用域:var与this,javascript,scope,scoping,Javascript,Scope,Scoping,我似乎无法理解JavaScript变量作用域的具体情况。与我发现的其他示例和问题不同,我对嵌套函数的范围感兴趣 我在学校树立了一个榜样。有关部分如下: function MyObject() { var self = this; var a = 1; this.b = 2; var innerMethod = function() { //1 and 2: direct reference logMessage("a = " +

我似乎无法理解JavaScript变量作用域的具体情况。与我发现的其他示例和问题不同,我对嵌套函数的范围感兴趣

我在学校树立了一个榜样。有关部分如下:

function MyObject() {
    var self = this;

    var a = 1;
    this.b = 2;

    var innerMethod = function() {
        //1 and 2: direct reference
        logMessage("a = " + a); // a = 1
        //logMessage("b = " + b); // Error: b is not defined

        //3 and 4: using this
        logMessage("this.a = " + this.a); // this.a = undefined
        logMessage("this.b = " + this.b); // this.b = undefined

        //5 and 6: using self
        logMessage("self.a = " + self.a); // self.a = undefined
        logMessage("self.b = " + self.b); // self.b = 2
    }
}
现在,我明白了对
a
的引用直接起作用。 我还了解到,消息3和4(
this.a
this.b
)将失败,因为
this
指的是内部功能。我也知道第6行是有效的,因为我保存了对原始对象的引用

我不明白的是:

  • 为什么消息1和消息2不一样
  • 为什么消息5和消息6不一样

    • 这是作用域的问题,创建函数时,它们会保存周围环境(包括变量)

      因此,当创建
      innerMethod
      时,它可以看到变量
      self
      a

      一个重要的概念是,范围是在声明函数时创建的,而不是在调用函数时创建的

      在您的案例1中,
      b
      未被声明(此对象的名称不同)


      在案例5和案例6中,您没有创建
      self。一个
      变量就是一个变量。它在
      innerMethod
      (这只是一个嵌套函数)的范围内可见,正如
      a
      ,这就是它的声明方式(即JavaScript具有词法范围规则,内部函数可以看到它们在其中定义的函数的变量)

      MyObject
      构造函数的本地范围不同

      您已经看到,
      self
      MyObject
      this
      的别名,并且
      innerMethod
      在其自身范围内覆盖了
      this
      。但是,由于
      this
      不是函数作用域的别名,因此
      self.a
      this.a
      都不会在这里工作

      要更严格地解释词汇范围,您可以从维基百科开始:


      您可以阅读ECMA标准中的执行上下文和标识符解析规则

      主要原因是
      self
      innerMethod范围内不等于
      this
      <代码>此
      是引用函数所有者的关键字。对于innerMethod,它不是实例方法,它属于窗口

      function MyObject() {
          var self = this;
      
          var innerMethod = function() {
              alert("inner method, self == this?: " + self == this); // false
              alert("inner method: " + this); // [object Window]
              alert("closest constructor name of in prototype chain ?: "+ this.__proto__.constructor.name); // Window
          }
      
          this.outerMethod = function(){
              innerMethod(); 
              alert("outer method: " + this); // [object MyObject]
              alert("closest constructor name in prototype chain?: "+ this.__proto__.constructor.name); // MyObject
          }
      }
      
      var o = new MyObject();
      o.outerMethod();
      

      你可以在

      上玩,为什么它们都一样?似乎您在用Java或其他语言进行类比,其中
      这个
      名称空间是隐式的,而JS则不是这样。@FabriciomattéI可能无意识地这样做(双关语)。我不明白作用域是否应该使变量/成员自动暴露给内部成员。似乎不是这样,因为它不一致。@Alpha您正在将变量(
      var a=5;
      )与不是变量而是对象属性的事物(
      this.b=10;
      设置
      this
      引用的对象的属性
      b
      )。这些东西不一样,因此行为也不相同。我想用一种简单的方法来解释它,但基本上JS的词法作用域不会像你说的那样自动将成员暴露给内部成员。@Alpha如果你调用了
      innerMethod.call(这个),请看4的变化
      this.method=innerMethod
      (新的MyObject()).method()
      我更喜欢第三段中的术语,而不是
      函数范围
      ,并声明第一段背后的原因是JS有。但是解释得很好。我已经合并了您的一些更改,但是我对用VariableEnvironment交换函数范围犹豫不决。尽管它在技术上更为正确,但我相信函数作用域这个概念对于来自其他语言的程序员来说更为广为人知。我已将您的链接添加到ECMA标准。