Node.js:全局作用域中'this'的使用令人困惑

10

我最近一直在尝试使用node.js,然后我遇到了一个有关在模块全局作用域中使用this的奇怪行为。

this在模块全局作用域中被绑定到module.exports。

console.log(this === exports); // -> true

但是在方法作用域中,this被绑定到全局对象:

(function() { console.log(this === global); })(); // -> true

这也导致了这种令人困惑的行为:

this.Foo = "Weird";
console.log(Foo); // -> throws undefined

(function() { this.Bar = "Weird"; })();
console.log(Bar); // -> "Weird"

我猜解决方案是永远不要在全局范围内使用this,而是明确使用extendsglobal代替,但这背后是否有逻辑或者是node.js的一个错误或限制呢?

3个回答

7

这背后的“逻辑”在于,this的值始终取决于函数被调用的方式。

在您的情况下,您有一个自执行的匿名函数,因此this始终引用全局对象(非严格模式)或undefined(ES5严格模式)。

如果您想访问“外部”的this值,则可以在执行该函数之前存储引用,例如:

var outerScope = this;

(function() { outerScope.Bar = "Weird"; })();
console.log(Foo); // -> throws undefined

或者您可以自己重新绑定函数的作用域,例如:.bind()
(function() { this.Bar = "Weird"; }).bind(this)();

1
如果在全局范围内,this绑定到global而不是extends,那么会不会更清晰一些呢?我期望在全局范围和静态函数范围中,this的含义是相同的。 - Jeff Cyr
@SelflessCoder:老实说,我不知道为什么这个绑定到了“exports”对象上。但是一般来说最好不要用数据覆盖全局对象。所以,请记住任何函数上下文都可能有不同的“this”值,这取决于它被如何调用。 - jAndy
正如jAndy所说,这基本上是javascript中this的工作原理。我没有阅读任何关于它的讨论,但我猜在全局范围内有this === exports应该类似于在浏览器的全局范围内有this === window。在我看来,在全局范围内有this === global就没那么有意义了,因为那意味着this.foo === foo - Linus Thiel
@Linus G Thiel:在浏览器中,最后一个表达式是true,所以我认为实际上this === global更合乎逻辑。 - pimvdb

1

我不知道Node.js团队是否确切地有这个意图,但如果没有的话,我会感到惊讶。考虑在浏览器(例如Chrome)的开发控制台中运行的此示例:

var x = function(){console.log(this)}
a = {}
a.x = x
a.xx = function(){x()}

a.x()
>> Object
a.xx()
>> DOMWindow
x()
>> DOMWindow

正如您所看到的,执行一个方法而不指定其上下文会将上下文设置为全局上下文。在这种情况下,上下文是DOMWindow对象。

当您在模块内部时,您的上下文是该模块,但是如果在没有使用.call或.apply或obj.指定上下文的情况下在其中执行方法,则会使用全局上下文global,而不是本地上下文module.exports


1
在实现一个简单的CommonJS模块时,我不得不考虑如何处理模块全局范围内的this,因为规范中没有涉及到这个问题。
一开始我将this设置为exports对象,因为我认为这可能会有用,但后来发现了一些代码需要将其“模块化”,使用了this以获取全局对象的句柄,所以我将this再次更改回全局对象,以尽可能地提供与“正常”环境相似的模块代码环境。
我们只能猜测node为什么要这样设置(或者问作者),但我的猜测是这样做似乎是一个有用的想法,类似于在node中给module对象添加一个exports属性并在模块的实际exports中反映出来的方式(这种行为也不在规范中,但也不违背它)。
关于你问题中有关函数中 this 引用全局变量的部分,正如其他答案所解释的那样,这只是 this 的工作方式;这不是特定于节点的行为,而是奇怪的 JavaScript 行为。

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