在 Github 上查看 CoffeeScript的源代码时,我注意到大多数(如果不是全部)module定义如下:
(function() {
...
}).call(this);
这种模式看起来像是将整个module包装在一个匿名函数中并调用自身。
这种方法的优点(和缺点)是什么?是否有其他方法可以实现相同的目标?
在 Github 上查看 CoffeeScript的源代码时,我注意到大多数(如果不是全部)module定义如下:
(function() {
...
}).call(this);
这种模式看起来像是将整个module包装在一个匿名函数中并调用自身。
这种方法的优点(和缺点)是什么?是否有其他方法可以实现相同的目标?
Harmen 的回答非常好,但让我详细说明 CoffeeScript 编译器在何处完成此操作以及原因。
当你用 编译一些东西时coffee -c foo.coffee
,你总是会得到一个foo.js
看起来像这样的:
(function() {
...
}).call(this);
这是为什么?好吧,假设你把这样的作业
x = 'stringy string'
在foo.coffee
. 当它看到时,编译器会问:是否x
已经存在于这个范围或外部范围内?如果没有,它会var x
在 JavaScript 输出中该范围的顶部放置一个声明。
现在假设你写
x = 42
in bar.coffee
,编译两者,并连接foo.js
以bar.js
进行部署。你会得到
(function() {
var x;
x = 'stringy string';
...
}).call(this);
(function() {
var x;
x = 42;
...
}).call(this);
所以x
infoo.coffee
和x
inbar.coffee
是完全隔离的。这是 CoffeeScript 的一个重要部分:变量永远不会从一个 .coffee 文件泄漏到另一个文件,除非明确导出(通过附加到共享全局或exports
在 Node.js 中)。
您可以通过使用-b
("bare") 标志来覆盖它coffee
,但这只能在非常特殊的情况下使用。如果你在上面的例子中使用它,你得到的输出将是
var x;
x = 'stringy string';
...
var x;
x = 42;
...
这可能会产生可怕的后果。为了测试这个自己,尝试添加setTimeout (-> alert x), 1
在foo.coffee
。请注意,您不必自己连接两个 JS 文件——如果您使用两个单独的<script>
标签将它们包含在一个页面上,它们仍然可以有效地作为一个文件运行。
通过隔离不同module的作用域,CoffeeScript 编译器使您免于担心项目中的不同文件是否可能使用相同的局部变量名称。这是 JavaScript 世界中的常见做法(例如,请参阅jQuery 源代码,或几乎所有 jQuery 插件)——CoffeeScript 会为您处理。
这种方法的好处是它创建了私有变量,所以不会与变量名发生任何冲突:
(function() {
var privateVar = 'test';
alert(privateVar); // test
})();
alert(typeof privateVar); // undefined
的添加.call(this)
使this
关键字引用的值与其在函数外引用的值相同。如果不加,this
关键字会自动引用全局对象。
一个显示差异的小例子如下:
function coffee(){
this.val = 'test';
this.module = (function(){
return this.val;
}).call(this);
}
var instance = new coffee();
alert(instance.module); // test
function coffee(){
this.val = 'test';
this.module = (function(){
return this.val;
})();
}
var instance = new coffee();
alert(typeof instance.module); // undefined
这是与此类似的语法:
(function() {
}());
这称为立即函数。该函数被定义并立即执行。这样做的优点是您可以将所有代码放在这个块中,并将函数分配给单个全局变量,从而减少全局命名空间污染。它在函数内提供了一个很好的包含范围。
这是我在编写module时使用的典型模式:
var MY_MODULE = (function() {
//local variables
var variable1,
variable2,
_self = {},
etc
// public API
_self = {
someMethod: function () {
}
}
return _self;
}());
不确定缺点可能是什么,如果其他人知道任何缺点,我很乐意了解它们。