模式的CoffeeScript的模块

问题描述:

在回顾source code for CoffeeScript on Github,我注意到大部分,如果不是全部的模块定义如下:模式的CoffeeScript的模块

(function() { 
    ... 
}).call(this); 

这种模式看起来像它包装的整个模块中,一个匿名函数和自称。

这种方法的优点(和缺点)是什么?还有其他方法可以实现相同的目标吗?

+5

请注意,您正在查看生成的JavaScript,而不是写入的咖啡文本。 – Dykam 2011-03-06 16:28:57

Harmen的回答非常好,但让我详细介绍一下CoffeeScript编译器和原因所做的工作。

当您编译coffee -c foo.coffee的东西,你总是会得到foo.js,看起来像这样:

(function() { 
    ... 
}).call(this); 

这是为什么?那么,假设你把一个任务如

x = 'stringy string' 

foo.coffee。当它看到的时候,编译器会问:x已经存在于这个范围内,还是外部范围?如果不是,则在JavaScript输出中将该范围的顶部放置一个var x声明。

现在假设你写

x = 42 

bar.coffee,编译两者并用bar.js部署串联foo.js。你会得到

(function() { 
    var x; 
    x = 'stringy string'; 
    ... 
}).call(this); 
(function() { 
    var x; 
    x = 42; 
    ... 
}).call(this); 

所以在foo.coffeexbar.coffeex彼此完全隔离。这是CoffeeScript的一个重要部分:变量不会从一个.coffee文件泄露到另一个文件,除非明确导出(通过连接到共享全局或在Node.js中连接到exports)。

您可以通过使用-b(“裸”)标志覆盖coffee,但这应该只用于非常特殊的情况。如果你使用上面的例子,你会得到的输出将是

var x; 
x = 'stringy string'; 
... 
var x; 
x = 42; 
... 

这可能会有可怕的后果。要亲自测试,请尝试在foo.coffee中添加setTimeout (-> alert x), 1。并且请注意,您不必自己连接两个JS文件 - 如果您使用两个独立的<script>标记将它们包含在页面中,它们仍然可以有效地作为一个文件运行。

通过隔离不同模块的作用域,CoffeeScript编译器让您不必担心项目中的不同文件是否可能使用相同的本地变量名称。这是JavaScript世界中的常见做法(例如,请参见jQuery source,或者几乎任何jQuery插件)-CoffeeScript都会为您提供帮助。

+1

优秀的战略! – lo5 2011-03-07 07:26:56

+4

您的答案写得很好,真的可以帮助我更好地理解CoffeeScript(和JavaScript)......谢谢! – 2011-03-09 13:15:45

+1

heck玩这样的全局变量的人是谁?没有课堂或封闭,我不会想到做任何事情。不得不明确出口是令人厌恶的。 – Duke 2011-09-22 08:58:00

这种方法的好处是,它创建私有变量,所以不会有变量名的任何冲突:

(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 
+0

很好的答案。谢谢你的帮助! – 2011-03-09 13:14:06

这类似于语法这样:

(function() { 

}()); 

其被称为即时功能。该功能被定义并立即执行。这个优点是你可以将所有的代码放在这个块中,并且将这个函数分配给一个单独的全局变量,从而减少全局命名空间的污染。它在函数中提供了一个很好的包含范围。

这是典型的图案写入模块时,我使用:

var MY_MODULE = (function() { 
    //local variables 
    var variable1, 
     variable2, 
     _self = {}, 
     etc 

    // public API 
    _self = { 
     someMethod: function() { 

     } 
    } 

    return _self; 
}()); 

不知道利弊可能是究竟是什么,如果别人知道的任何我会很乐意了解他们。

+0

我想说的是:1)你必须努力工作,以便在模块之间共享状态(至少在CoffeeScript中 - 在JavaScript中,你可以省略'var',但通常不鼓励,例如通过jsLint),以及2)额外的函数调用和字节有一小部分开销。但99%的时间,额外的安全是值得的。 – 2011-03-06 18:29:16

+0

不,这不是一个相关的答案。他正在问(函数(){})。具体调用(this),而不是(function(){})()。哈门的回答是关键的。 – 2011-10-12 10:38:53