3

使用以下习惯用法定义 CommonJS 模块是很常见的:

(function() {
   var logThis = function() { console.log(this); }
   module.exports = logThis;
}).call(this);

例如,Underscore.js就是这样做的。

我只花了半个小时和一位同事讨论他们为什么用call(this). 这将导致this闭包内部的值从调用者继承,而不是设置为全局对象。但是,当我在 Node.js 中对此进行测试时,this模块内部的值始终是全局对象,即使我像这样加载并运行它:

var bar = {};
bar.foo = function() { var foo = require("./foo"); foo(); }

我真的希望bar在控制台中看到该对象,但实际上我看到的是全局对象。然后我想到这可能是因为像 Underscore.js 这样的模块也用于 Web 上下文。但在那种情况下,它会被加载一个 <script> 标签,所以无论如何它this总是等于全局对象。

是什么赋予了?我确信使用这种结构是有原因的,但在这种特殊情况下,无论模块是在 Node.js 中还是在网页中使用,我都看不出实际的区别。

更新:为了澄清,我可以想到一些可能会产生影响的情况。例如,如果我说:

var bar = {}
var foo = require("./foo");
bar.foo = foo;
bar.foo();

(感谢@Pointy 纠正我原来的例子。)

我希望模块中的闭包在require()被调用时被评估,这意味着它this内部的值将绑定到全局对象,即使foo()随后作为“bar”的成员调用,也会将其写入控制台“ 目的。但是,即使在此示例中,我也会在控制台中看到“栏”对象。我想这this并没有像我预期的那样被关闭?

简而言之,我正在寻找一个示例,其中像 Underscore.js 这样的模块将具有不同的行为,因为它被包装在一个调用的闭包中,fn.call(this)而不是只是fn()在 Node.js 或网页中。

4

1 回答 1

4

您在“bar.foo”中对“foo”的调用是在没有任何上下文的情况下进行的,因此使用了全局上下文。this它在引用“bar”的函数内部的事实是不相关的;这不是 JavaScript 的工作方式。换句话说,唯一重要的是函数是如何被调用的,而不是在哪里被调用。

如果“bar.foo”看起来像这样:

bar.foo = function() { require("./foo"); foo.call(this); }

然后你会在控制台中看到“栏”。或者,您可以这样做:

var bar = {};
require("./foo");
bar.foo = foo; 

然后调用bar.foo()也会记录“bar”对象。(这在 Node 中真的有效吗?也就是说,我认为require()返回了一个对象,并且它不只是将事物留在全局范围内。不过,我是 Node 的新手。)

编辑-好的,感谢您的更新。因此,我的示例将更正如下。首先,我认为你的模块应该是这样的:

(function() {
   var logThis = function() { console.log(this); }
   module.exports.logThis = logThis;
}).call(this);

也就是说,我认为您想探索“logThis”函数,因此需要将它作为命名属性绑定到“exports”对象。

然后:

var bar = {};
var foo = require("./foo");
// At this point, foo.logThis is the function 
bar.foo = foo.logThis;
// Now the "foo" property of "bar" is a reference to the same function
bar.foo(); // logs the "bar" object

var fee = { fie: foo.logThis };
fee.fie(); // logs the "fee" object
于 2012-08-01T16:45:44.440 回答