JavaScript本地作用域:var vs. this

9

我似乎无法理解JavaScript变量作用域的一个特定案例。与我发现的其他示例和问题不同,我对嵌套函数的作用域感兴趣。

我在这个JSFiddle上设置了一个示例。相关部分如下:

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.athis.b)会失败,因为 this 指向内部函数。我还明白第 6 行能工作是因为我保存了对原始对象的引用。

我不明白的是:

  • 为什么消息 1 和 2 的工作方式不同?
  • 为什么消息 5 和 6 的工作方式不同?

5
为什么它们会工作得像呢?看起来你在类比Java或其他语言,其中“this”命名空间是隐式的,但JS并非如此。 - Fabrício Matté
3
@Alpha,你正在将变量(var a = 5;)与不是变量但是对象属性的东西进行比较(this.b = 10;会将this指向的对象的属性b设置为10)。这些事物并不相同,因此它们的行为也不相同。请注意区分。 - Niko
我在考虑一个简单的方式来解释它,但基本上JS的词法作用域不会自动将成员暴露给内部成员,就像你所说的那样。 - Fabrício Matté
1
@Alpha 看看如果你使用 innerMethod.call(this); 或者 this.method = innerMethod; 以及 (new MyObject()).method();,4会如何变化。 - Paul S.
@PaulS。修改this引用有点超出问题的范围,但是如果OP或任何人想阅读,这是一篇不错的文章:神话般的方法 作者是TJ Crowder - Fabrício Matté
显示剩余3条评论
3个回答

4
a变量就是一个变量。它在innerMethod的作用域中可见(这只是一个嵌套函数),作为a,这就是它的声明方式(即JavaScript具有词法作用域规则,内部函数可以看到定义在其中的函数的变量)。 thisMyObject构造函数的局部作用域不同。
你已经看到selfMyObjectthis的别名,并且innerMethod已经在其自己的作用域中覆盖了this。然而,由于this不是函数作用域的别名,因此self.athis.a都无法在这里工作。
关于词法作用域的更严谨解释,您可以从维基百科开始阅读:http://en.wikipedia.org/wiki/Scope_(computer_science) 您可以在ECMA标准http://es5.github.com/#x10.3中阅读有关执行上下文和标识符解析规则的内容。

我更喜欢在你的第三段中使用术语VariableEnvironment而不是function scope,并说明第一段背后的原因是JS具有词法作用域。但是解释得非常好。 - Fabrício Matté
我已经加入了你的一些更改,但是我还不确定是否要用函数作用域来替换VariableEnvironment。虽然从技术上讲这样更正确,但我认为函数作用域是一个更适合来自其他编程语言的程序员理解的概念。我已经将你提供的ECMA标准链接添加进去了。 - thebjorn

1
这是一个作用域问题,当函数被创建时,它们会保存其周围环境(包括变量)。
因此,当创建innerMethod时,它可以看到变量selfa
一个重要的概念是,作用域是在函数声明时创建的,而不是在调用时创建的。
在你的第一种情况中,b没有被声明(this对象不同)。
在第5和6种情况中,你没有创建self.a

0
主要原因是在innerMethod的作用域中,self不等于thisthis是一个关键字,用于引用函数的所有者。对于innerMethod来说,它不是一个实例方法,而是属于Window对象的。
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();

你可以在这里


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接