常见的 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 这样的模块也在网络上下文中使用。但在那种情况下,它会加载一个 <script> 标签,所以 this
将始终等于全局对象。
什么给了?我确信使用这种结构是有原因的,但我看不出在这种特殊情况下模块是在 Node.js 中还是在网页中使用的实际区别。
更新:澄清一下,我能想到一些这可以发挥作用的案例。例如,如果我说:
var bar = {}
var foo = require("./foo");
bar.foo = foo;
bar.foo();
(感谢@Pointy 纠正了我原来的例子。)
我希望在调用 require()
时评估模块中的闭包,这意味着其中的 this
的值将绑定(bind)到全局对象,即使 foo()
随后作为“bar”对象的成员被调用,它也会被写入控制台。但是,即使在这个例子中,我也在控制台中看到了“bar”对象。我猜 this
没有像我预期的那样绑定(bind)到闭包?
简而言之,我正在寻找一个示例,其中像 Underscore.js 这样的模块将具有不同的行为,因为它被包裹在用 fn.call(this)
而不是仅仅调用的闭包中fn()
,在 Node.js 或网页中。
最佳答案
您在“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”函数,所以它需要作为命名属性绑定(bind)到“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
关于javascript - 定义 CommonJS 模块时的 fn.call(this) 与 fn(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11763819/