如何在CoffeeScript中定义全局变量?


317

在Coffeescript.org上:

bawbag = (x, y) ->
    z = (x * y)

bawbag(5, 10) 

将编译为:

var bawbag;
bawbag = function(x, y) {
  var z;
  return (z = (x * y));
};
bawbag(5, 10);

通过在node.js下的coffee-script进行编译,可以这样包装:

(function() {
  var bawbag;
  bawbag = function(x, y) {
    var z;
    return (z = (x * y));
  };
  bawbag(5, 10);
}).call(this);

文件说:

如果要创建供其他脚本使用的顶级变量,请将它们作为属性附加到窗口或CommonJS中的exports对象上。如果您同时针对CommonJS和浏览器,那么存在运算符(见下文)为您提供了一种可靠的方法来确定将它们添加到何处:root = exports?这个

然后如何在CoffeeScript中定义全局变量。“将它们作为窗口的属性附加”是什么意思?


4
请注意,使用全局变量是不好的c2.com/cgi/wiki?GlobalVariablesAreBad,甚至被认为是有害的c2.com/cgi/wiki?GotoConsideredHarmful。实际上,根本没有理由在JavaScript中使用它们,因为您拥有诸如闭包之类的强大功能,可以解决您正在使用全局变量来解决的大多数问题。
叶夫根尼(Evgeny)

9
@Evgeny虽然我在这里同意您的意见,但在某些情况下,有必要创建一个中央的“ app”对象并为其附加模块。
jackyalcine

1
中心对象可以保存到现有的全局状态对象中,例如window对象或exports对象。无需创建全局变量。
叶夫根尼(Evgeny)2012年

9
@Evgeny全局变量保存为window(或global在nodejs上)对象的属性
shesek 2013年

21
是的,拥有全局变量并不是一件坏事。愚蠢的做法是不加考虑地使您的应用程序崩溃。声明一个并将其用作像jQuery之类的适配器工厂或某种名称空间确实是很常见的做法。
埃里克·雷彭

Answers:


419

由于coffee脚本没有var语句,因此它会自动将其插入coffee脚本中的所有变量,这样可以防止已编译的JavaScript版本将所有内容泄漏到全局命名空间中

因此,由于没有办法故意从咖啡脚本方面使某些内容“泄漏”到全局名称空间中,因此您需要将全局变量定义为全局对象的属性。

将它们作为属性附加在窗口上

这意味着您需要执行类似之类的操作window.foo = 'baz';来处理浏览器的情况,因为那里的全局对象window

Node.js

在Node.js中没有window对象,而是有一个exports对象传递给包装Node.js模块的包装器(请参阅:https : //github.com/ry/node/blob/master/src/node.js# L321),因此在Node.js中,您需要做的是exports.foo = 'baz';

现在,让我们看看它在您的文档中的报价中指出的内容:

...针对CommonJS和浏览器:root = exports?这个

显然,这是咖啡脚本,因此让我们看一下它实际编译成的内容:

var root;
root = (typeof exports !== "undefined" && exports !== null) ? exports : this;

首先,它将检查是否exports已定义,因为尝试在JavaScript中引用不存在的变量会否则产生SyntaxError(与结合使用时除外typeof

因此,如果exports存在,则在Node.js(或写得不好的WebSite ...)中就是这种情况,根将指向exports,否则指向this。那是this什么

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

使用.call上的功能将绑定this功能里面第一个参数传递,在浏览器的情况下,this现在会是window对象,在Node.js的的情况下,这将是全球范围内它也可作为global对象。

但是,由于您require在Node.js中具有该函数,因此无需global在Node.js中为该对象分配某些内容,而只需将exports其分配给该对象,然后该对象将由require函数返回。

咖啡脚本

经过所有这些解释之后,您需要执行以下操作:

root = exports ? this
root.foo = -> 'Hello World'

这将foo在全局名称空间中声明我们的函数(无论发生了什么)。
就这样 :)


1
@IvoWetzel -有什么之间的区别globalGLOBAL以及root在Node.js的对象?
2011年

1
试图在JavaScript中引用不存在的变量,否则会产生SyntaxError,不是ReferenceError吗?
alex 2012年

12
或更短的时间:(exports ? this).foo = -> 'Hello World'
Dane O'Connor 2012年

3
this.foo通常是!= window.foo,尽管如果您是'this'上下文已经是一个对象。这是一个令人困惑的语法。
凯文(Kevin)

1
虽然我同意使用global = exports ? this。声称“在Node.js情况下将是全局上下文...”的说法是错误的,因为该this变量在需要时或由node.js运行时被评估为模块范围。因此,如果您希望为其设置道具将使其在全球范围内均可使用,那么您将感到失望。如果您确实想在node.js上下文中进行全局设置,则需要使用global变量,而不是this
KFL 2014年

58

对我来说,@atomicules似乎是最简单的答案,但我认为它可以进一步简化。您需要@在要成为全局对象的任何内容之前放置一个,以便它可以编译this.anythingthis引用全局对象。

所以...

@bawbag = (x, y) ->
    z = (x * y)

bawbag(5, 10)

编译为...

this.bawbag = function(x, y) {
  var z;
  return z = x * y;
};
bawbag(5, 10);

并在node.js给定的包装器内部和外部工作

(function() {
    this.bawbag = function(x, y) {
      var z;
      return z = x * y;
    };
    console.log(bawbag(5,13)) // works here
}).call(this);

console.log(bawbag(5,11)) // works here

7
但是,如果您已经在另一个示波器中,这将不起作用,对吗?因为那样的话this不再引用全局对象
Sherwin Yu

1
没错,因此您可以在适当的范围内定义变量(并在其他范围内使用它),或者定义在window.myVariable任何地方都可以使用的变量。
比利·穆恩

2
您不需要定义另一个变量,只需使用它即可=>代替,->它指示coffeescript在this / global名称空间下创建函数
Ricardo Villamil 2013年

2
这非常有用,现在我可以在单独的咖啡脚本中创建全局对象和函数
Diego Fernando Murillo Valenci 2014年

这样好多了。将JS转移到CS需要我更改许多函数调用以使用window对象,现在我可以还原它了
casraf 2014年

33

Ivo钉上了它,但是我要提到有一个可以使用的肮脏技巧,尽管如果您要获取样式点,我不建议您使用它:您可以通过反引号将JavaScript代码直接转入CoffeeScript中。

然而,这就是为什么这通常是一个坏主意的原因:CoffeeScript编译器没有意识到这些变量,这意味着它们将不遵守正常的CoffeeScript范围规则。所以,

`foo = 'bar'`
foo = 'something else'

编译为

foo = 'bar';
var foo = 'something else';

现在您foo在不同范围内拥有两个。如Ivy所描述的,没有引用全局对象就无法从CoffeeScript代码修改全局的 方法foo

当然,这仅是一个问题,如果您foo在CoffeeScript中进行了赋值-如果foo在为其赋初始值(即,它是一个全局常量)后变为只读,则嵌入式JavaScript解决方案可能还算可以接受(尽管不建议)。


1
因为我将Titanium与CoffeeScript一起使用,所以这对我来说是一个有用的解决方案。导出和窗口对象在那里不存在。
Pier-Olivier Thibault,2012年

实际上,这只是局部foo变量,因为var提升(JS会向前扫描所有var声明,并将它们解释为位于函数顶部)
Kornel 2012年

@porneL你是正确的;我选了一个不好的例子。关键是,CoffeeScript编译器不会对反引号转义的JavaScript进行任何分析,因此您可以获得奇数输出。
Trevor Burnham 2012年

2
@ Pier-OlivierThibault如果您想在Titanium中使用Globals,则可以使用Ti.App.myGlobalVar =“ ImAGlobalVar”,并且不需要反引号
Jakob Lnr 2012年

至少对于Node.js,这是正确的答案。这样做expect = require('chai').expect;使expect变量在我所有的测试文件中都可用!
pocesar

11

通过node.js下的coffee-script编译代码时,可以传递-b选项。编译后的代码将与coffeescript.org上的相同。


怎么样?我在哪里放置-b选项?
哈利

1
@Harry- -b/ --bare直接在coffee命令之后。
ocodo 2014年

9

添加到Ivo Wetzel的答案中

似乎有一种简写语法exports ? this,我只能在Google网上论坛发帖中找到记录/提及的内容。

即在网页中,要使函数在全局范围内可用,您可以再次使用@前缀声明函数:

<script type="text/coffeescript">
    @aglobalfunction = aglobalfunction = () ->
         alert "Hello!"
</script>

<a href="javascript:aglobalfunction()" >Click me!</a>

9
@aglobalfunction中的“ @”被“ this。”简单替换,因此编译为“ this.aglobalfunction”。之所以可行,是因为coffeescript包装函数的作用域(如果应用)是全局作用域。
克里斯,

9

我认为您想要实现的目标可以像这样简单地完成:

编译coffeescript时,请使用“ -b”参数。

-b/ --bare 编译没有顶层函数安全包装器的JavaScript。

所以像这样: coffee -b --compile somefile.coffee whatever.js

就像在CoffeeScript.org网站中一样,这将输出您的代码。


7

如果您是坏人(我是坏人。),您可以像这样简单: (->@)()

就像

(->@)().im_a_terrible_programmer = yes
console.log im_a_terrible_programmer

这个作品,因为调用时ReferenceFunction“裸”(即func(),而不是new func()obj.func()),一些通常被称为“函数调用调用模式”,总是结合this为使全局对象执行上下文

上面的CoffeeScript可以简单地编译为(function(){ return this })(); 因此,我们正在执行该行为以可靠地访问全局对象。


这太棒了!
metalim 2016年

唯一对我有用的东西。讨厌CoffeeScript。
pcv

喜欢CoffeeScript。到目前为止,它是目前最好的编程语言。太糟糕了,它是作为一个业余项目创建和维护的,导致其使用模式混乱和愚蠢。
metalim

3

由于coffeescript很少单独使用,因此您可以使用globalnode.js或browserify提供的变量(以及任何后代,例如coffeeify,gulp构建脚本等)。

在node.js中global是全局名称空间。

在browserify global中等于window

所以就:

somefunc = ->
  global.variable = 123
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.