【问题标题】:Pattern for CoffeeScript modules [duplicate]CoffeeScript 模块的模式 [重复]
【发布时间】:2011-07-09 20:36:02
【问题描述】:

在查看source code for CoffeeScript on Github 时,我注意到大多数(如果不是全部)模块的定义如下:

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

这种模式看起来像是将整个模块包装在一个匿名函数中并调用自身。

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

【问题讨论】:

  • 请注意,您正在查看生成的 javascript,而不是编写的咖啡脚本。

标签: javascript node.js coffeescript


【解决方案1】:

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.jsbar.js 连接以进行部署。你会得到

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

所以foo.coffee 中的xbar.coffee 中的x 完全相互隔离。这是 CoffeeScript 的一个重要部分:变量永远不会从一个 .coffee 文件泄漏到另一个文件,除非显式导出(通过附加到共享全局,或 Node.js 中的 exports)。 p>

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

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

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

通过隔离不同模块的范围,CoffeeScript 编译器让您不必担心项目中的不同文件是否可能使用相同的局部变量名。这是 JavaScript 世界中的常见做法(例如,请参阅 jQuery source 或几乎任何 jQuery 插件)—CoffeeScript 会为您处理它。

【讨论】:

  • 您的回答写得很好,确实帮助我更好地理解 CoffeeScript(和 JavaScript)...谢谢!
  • 谁在玩这样的全局变量?如果没有课程或闭包,我不会想到做任何事情。必须显式导出是令人讨厌的。
  • 我在开发angularjs时开始使用coffeescript。 Andularjs 的策略是 KILL 所有全局变量,所以 -b 标记在我的情况下就足够了 :)
【解决方案2】:

这种方法的好处是它创建了私有变量,因此不会与变量名冲突:

(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

【讨论】:

  • 好答案。感谢您的帮助!
【解决方案3】:

这与下面的语法类似:

(function() {

}());

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

这是我在编写模块时使用的典型模式:

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

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

       }
    }

    return _self;
}());

不确定到底有什么缺点,如果其他人知道我会很乐意了解它们。

【讨论】:

  • 我会说缺点是 1) 你必须更加努力地在模块之间共享状态(至少在 CoffeeScript 中——在 JavaScript 中,你可以省略 var,但通常不鼓励这样做,例如通过jsLint),和2)额外的函数调用和字节有一点点开销。但 99% 的情况下,额外的安全是值得的。
  • 不,这不是一个相关的答案。他是在问 (function(){}).call(this),而不是 (function(){})() 你举例说明的那样。哈门的回答很中肯。
猜你喜欢
  • 1970-01-01
  • 2013-10-15
  • 2014-02-01
  • 2015-11-01
  • 2012-01-17
  • 1970-01-01
  • 2018-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多