在 Github 上查看 CoffeeScript的源代码时,我注意到大多数(如果不是全部)模块的定义如下:
(function() {
...
}).call(this);
这种模式看起来像是将整个模块包装在一个匿名函数中并调用自身。
这种方法的优点(和缺点)是什么?还有其他方法可以实现相同的目标吗?
在 Github 上查看 CoffeeScript的源代码时,我注意到大多数(如果不是全部)模块的定义如下:
(function() {
...
}).call(this);
这种模式看起来像是将整个模块包装在一个匿名函数中并调用自身。
这种方法的优点(和缺点)是什么?还有其他方法可以实现相同的目标吗?
Harmen 的回答非常好,但让我详细说明一下 CoffeeScript 编译器在哪里执行此操作以及原因。
当你用 编译一些东西时coffee -c foo.coffee
,你总是会得到一个foo.js
看起来像这样的:
(function() {
...
}).call(this);
这是为什么?好吧,假设你把一个任务像
x = 'stringy string'
在foo.coffee
. 当它看到时,编译器会问:是否x
已经存在于这个范围内,还是外部范围内?如果不是,它会var x
在 JavaScript 输出中该范围的顶部放置一个声明。
现在假设你写
x = 42
在 中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>
的标签将它们包含在一个页面上,它们仍然有效地作为一个文件运行。
通过隔离不同模块的作用域,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() {
}());
这称为立即函数。该函数被定义并立即执行。这样做的好处是您可以将所有代码放在此块中,并将函数分配给单个全局变量,从而减少全局命名空间污染。它在函数中提供了一个很好的包含范围。
这是我在编写模块时使用的典型模式:
var MY_MODULE = (function() {
//local variables
var variable1,
variable2,
_self = {},
etc
// public API
_self = {
someMethod: function () {
}
}
return _self;
}());
不知道确切的缺点是什么,如果其他人知道任何我很乐意了解它们。