在审核source code for CoffeeScript on Github时,我注意到大多数(如果不是全部)模块定义如下:
(function() {
...
}).call(this);
这种模式看起来像是将整个模块包装在一个匿名函数中并调用自身。
这种方法的优点和缺点是什么?还有其他方法可以实现相同的目标吗?
答案 0 :(得分:78)
Harmen的答案相当不错,但让我详细说明一下CoffeeScript编译器的工作原理以及原因。
使用coffee -c foo.coffee
编译内容时,您将始终获得foo.js
,如下所示:
(function() {
...
}).call(this);
为什么?好吧,假设你做了一个像
这样的作业x = 'stringy string'
foo.coffee
中的。当它看到时,编译器会问:此范围内是否已存在x
,还是外部范围?如果没有,它会在JavaScript输出中将该var x
声明放在该范围的顶部。
现在假设你写了
x = 42
在bar.coffee
中,编译两者,并将foo.js
与bar.js
连接起来进行部署。你会得到
(function() {
var x;
x = 'stringy string';
...
}).call(this);
(function() {
var x;
x = 42;
...
}).call(this);
因此x
中的foo.coffee
和x
中的bar.coffee
完全相互隔离。这是CoffeeScript的一个重要部分:变量永远不会从一个.coffee文件泄漏到另一个。除非明确导出(通过附加到共享全局或Node.js中的exports
)。
您可以使用-b
(“裸”)标记来覆盖此coffee
,但这只应在非常特殊的情况下使用。如果你在上面的例子中使用它,你得到的输出将是
var x;
x = 'stringy string';
...
var x;
x = 42;
...
这可能会产生可怕的后果。要自行测试,请尝试在setTimeout (-> alert x), 1
中添加foo.coffee
。请注意,您不必自己连接两个JS文件 - 如果您使用两个单独的<script>
标记将它们包含在页面中,它们仍然可以作为一个文件有效运行。
通过隔离不同模块的范围,CoffeeScript编译器可以避免担心项目中的不同文件是否可能使用相同的本地变量名称。这是JavaScript世界中的常见做法(例如,参见jQuery source,或者只是关于任何jQuery插件) - -CoffeeScript会为您处理它。
答案 1 :(得分:19)
这种方法的好处是它创建了私有变量,因此不会与变量名冲突:
(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
答案 2 :(得分:1)
这与此类似:
(function() {
}());
被称为立即函数。该函数立即定义并执行。这方面的优点是您可以将所有代码放在此块中,并将该函数分配给单个全局变量,从而减少全局命名空间污染。它在函数中提供了一个很好的包含范围。
这是我在编写模块时使用的典型模式:
var MY_MODULE = (function() {
//local variables
var variable1,
variable2,
_self = {},
etc
// public API
_self = {
someMethod: function () {
}
}
return _self;
}());
不确定缺点可能是什么,如果其他人知道任何我会很乐意了解它们。