Wie definiere ich globale Variablen in CoffeeScript?

317

Auf Coffeescript.org:

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

bawbag(5, 10) 

würde kompilieren zu:

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

Das Kompilieren über ein Kaffeeskript unter node.js umschließt Folgendes:

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

Die Ärzte sagen:

Wenn Sie Variablen der obersten Ebene für andere zu verwendende Skripts erstellen möchten, hängen Sie sie als Eigenschaften im Fenster oder im Exportobjekt in CommonJS an. Mit dem existenziellen Operator (siehe unten) können Sie zuverlässig herausfinden, wo Sie sie hinzufügen können, wenn Sie sowohl auf CommonJS als auch auf den Browser abzielen: root = exportiert? diese

Wie definiere ich dann globale Variablen in CoffeeScript? Was bedeutet "sie als Eigenschaften im Fenster anhängen"?

Handweberei
quelle
4
Beachten Sie, dass die Verwendung globaler Variablen schlecht ist ( c2.com/cgi/wiki?GlobalVariablesAreBad ) und sogar als schädlich eingestuft wird ( c2.com/cgi/wiki?GotoConsideredHarmful) . Und es gibt wirklich keinen Grund, sie überhaupt in JavaScript zu verwenden, da Sie großartige Funktionen wie Schließungen haben, die die meisten Probleme lösen können, für deren Lösung Sie globale Variablen verwenden.
Evgeny
9
@Evgeny Obwohl ich Ihnen hier zustimme, ist es in einigen Fällen erforderlich, ein zentrales 'App'-Objekt zu erstellen und Module daran anzuhängen.
Jackyalcine
1
Zentrale Objekte können in vorhandenen globalen Statusobjekten wie dem windowObjekt oder dem exportsObjekt gespeichert werden . Es müssen keine globalen Variablen erstellt werden.
Evgeny
9
@ Evgeny globale Variablen werden als Eigenschaften des window(oder globalauf nodejs) Objekts
gespeichert
21
Ja, es ist nicht schlecht, eine globale Var zu haben. Nur schlechte Praxis, um Ihre App gedankenlos mit ihnen abzuspritzen. Eine zu deklarieren und diese als Adapterfactory wie jQuery oder eine Art Namespace zu verwenden, ist eine gängige Praxis.
Erik Reppen

Antworten:

419

Da das Kaffeeskript keine varAnweisung enthält, fügt es diese automatisch für alle Variablen im Kaffeeskript ein. Auf diese Weise wird verhindert, dass die kompilierte JavaScript-Version alles in den globalen Namespace verliert .

Da es also keine Möglichkeit gibt, absichtlich etwas von der Kaffeeskript-Seite in den globalen Namespace zu "lecken" , müssen Sie Ihre globalen Variablen als Eigenschaften des globalen Objekts definieren .

Fügen Sie sie als Eigenschaften im Fenster hinzu

Dies bedeutet, dass Sie etwas tun müssen window.foo = 'baz';, das den Browser-Fall behandelt, da dort das globale Objekt das ist window.

Node.js

In Node.js gibt es kein windowObjekt, sondern das exportsObjekt, das an den Wrapper übergeben wird, der das Node.js-Modul umschließt (siehe: https://github.com/ry/node/blob/master/src/node.js#) L321 ), also müssten Sie in Node.js Folgendes tun exports.foo = 'baz';.

Lassen Sie uns nun einen Blick darauf werfen, was in Ihrem Zitat aus den Dokumenten steht:

... sowohl auf CommonJS als auch auf den Browser abzielen: root = exportiert? diese

Dies ist offensichtlich ein Kaffeeskript, also schauen wir uns an, was dies tatsächlich kompiliert:

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

Zuerst wird geprüft, ob dies exportsdefiniert ist, da der Versuch, auf eine nicht vorhandene Variable in JavaScript zu verweisen, andernfalls einen SyntaxError ergeben würde (außer wenn er mit verwendet wird typeof).

Wenn also exportsvorhanden ist, was in Node.js (oder in einer schlecht geschriebenen WebSite ...) der Fall ist, verweist root auf exports, andernfalls auf this. Also was ist this?

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

Wenn Sie .calleine Funktion verwenden, wird das thisInnere der Funktion an den ersten übergebenen Parameter gebunden. Wenn der Browser thisnun das windowObjekt ist, ist Node.js der globale Kontext, der auch als globalObjekt verfügbar ist .

Da Sie die requireFunktion in Node.js haben, müssen Sie dem globalObjekt in Node.js nichts zuweisen , sondern dem exportsObjekt zuweisen, das dann von der requireFunktion zurückgegeben wird.

Kaffee-Skript

Nach all dieser Erklärung müssen Sie Folgendes tun:

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

Dadurch wird unsere Funktion fooim globalen Namespace deklariert (was auch immer das sein mag).
Das ist alles :)

Ivo Wetzel
quelle
1
@IvoWetzel - Was ist der Unterschied zwischen den global, GLOBALund rootObjekten in node.js?
Aadit M Shah
1
versucht , eine nicht vorhandene Variable in JavaScript sonst eine Syntax ergeben würde zu referenzieren Meinst du nicht ReferenceError?
alex
12
Oder noch kürzer:(exports ? this).foo = -> 'Hello World'
Dane O'Connor
3
this.foo ist oft! = window.foo, wenn Sie 'dieser' Kontext sind, ist dies bereits ein Objekt. Dies ist eine verwirrende Syntax.
Kevin
1
Während ich mit der Verwendung einverstanden bin global = exports ? this. Die Behauptung, dass "im Fall von Node.js der globale Kontext wäre ..." falsch ist, weil die thisVariable, wenn sie von node.js benötigt oder ausgeführt wird, als Modulbereich ausgewertet wird. Wenn Sie also erwarten, dass das Einstellen von Requisiten weltweit verfügbar ist, werden Sie enttäuscht sein. Wenn Sie Dinge global im Kontext von node.js festlegen möchten, müssen Sie die globalVariable anstelle von verwenden this.
KFL
58

Mir scheint, @atomicules hat die einfachste Antwort, aber ich denke, es kann ein bisschen mehr vereinfacht werden. Sie müssen @vor allem, was global sein soll, ein setzen, damit es auf das globale Objekt kompiliert this.anythingund auf dieses thisverweist.

damit...

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

bawbag(5, 10)

kompiliert zu ...

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

und funktioniert innerhalb und außerhalb des von node.js angegebenen Wrappers

(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
Billy Moon
quelle
7
Aber das wird nicht funktionieren, wenn Sie sich bereits in einem anderen Bereich befinden, oder? Denn dann thisbezieht sich nicht mehr auf das globale Objekt
Sherwin Yu
1
Das ist richtig, also können Sie entweder Ihre Variable in einem geeigneten Bereich definieren (und in anderen verwenden) oder definieren, window.myVariablewelche überall funktionieren wird.
Billy Moon
2
Sie müssen keine andere Variable definieren, =>sondern verwenden stattdessen ->Coffeescript, um die Funktion unter dem Namespace this / global zu erstellen
Ricardo Villamil
2
Das war so hilfreich, jetzt kann ich globale Objekte und Funktionen in einem separaten
Kaffeeskript
Das ist viel besser. Um JS nach CS zu übertragen, musste ich viele Funktionsaufrufe ändern, um das Fensterobjekt zu verwenden. Jetzt kann ich das
zurücksetzen
33

Ivo hat es geschafft, aber ich werde erwähnen, dass es einen schmutzigen Trick gibt, den Sie verwenden können, obwohl ich ihn nicht empfehle, wenn Sie Stilpunkte anstreben: Sie können JavaScript-Code direkt in Ihr CoffeeScript einbetten, indem Sie ihn mit Backticks maskieren.

Dies ist jedoch der Grund, warum dies normalerweise eine schlechte Idee ist: Der CoffeeScript-Compiler kennt diese Variablen nicht, was bedeutet, dass sie nicht den normalen CoffeeScript-Gültigkeitsregeln entsprechen. Damit,

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

kompiliert zu

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

und jetzt hast du zwei foos in verschiedenen bereichen. Es gibt keine Möglichkeit, den globalen foo Code von CoffeeScript zu ändern, ohne auf das globale Objekt zu verweisen, wie Ivy beschrieben hat.

Dies ist natürlich nur dann ein Problem, wenn Sie fooin CoffeeScript eine Zuweisung vornehmen. Wenn fooder Anfangswert (dh eine globale Konstante) schreibgeschützt ist, ist der Ansatz der eingebetteten JavaScript-Lösung möglicherweise irgendwie akzeptabel (wenn auch immer noch) nicht empfohlen).

Trevor Burnham
quelle
1
Dies war eine nützliche Lösung für mich, da ich Titan mit CoffeeScript verwende. Exporte und Fensterobjekte sind dort nicht vorhanden.
Pier-Olivier Thibault
Eigentlich ist das nur eine lokale fooVariable, weil sie varhochgezogen wird (JS sucht nach allen varDeklarationen und interpretiert sie so, als ob sie oben auf der Funktion wären)
Kornel
@porneL Du bist richtig; Ich habe ein schlechtes Beispiel gewählt. Der Punkt ist, dass der CoffeeScript-Compiler keine Analyse von JavaScript mit Backtick-Escape durchführt, sodass Sie ungerade Ausgaben erhalten können.
Trevor Burnham
2
@ Pier-OlivierThibault Wenn Sie Globals in Titanium verwenden möchten, können Sie Ti.App.myGlobalVar = "ImAGlobalVar" verwenden und benötigen keine Backticks
Jakob Lnr
Dies ist die richtige Antwort, zumindest für Node.js. Doing expect = require('chai').expect;macht expectVariablen in allen meinen Testdateien verfügbar!
Pocesar
11

Sie können die Option -b übergeben, wenn Sie Code über ein Kaffeeskript unter node.js kompilieren. Der kompilierte Code ist der gleiche wie auf coffeescript.org.

Phongnh
quelle
Wie? Wo lege ich die Option -b ab?
Harry
1
@ Harry - -b/ --baregeht direkt nach dem coffeeBefehl.
ocodo
9

Zur Antwort von Ivo Wetzel hinzufügen

Es scheint eine Kurzsyntax dafür zu geben exports ? this, die ich nur in einem Google-Gruppenbeitrag dokumentiert / erwähnt finden kann .

Dh auf einer Webseite, um eine Funktion global verfügbar zu machen, deklarieren Sie die Funktion erneut mit einem @Präfix:

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

<a href="javascript:aglobalfunction()" >Click me!</a>
Atome
quelle
9
Das '@' in @aglobalfunction wird einfach durch 'this.' Ersetzt, sodass zu 'this.aglobalfunction' kompiliert wird. Dies funktioniert, da der Bereich der Coffeescript-Wrapper-Funktion (falls angewendet) der globale Bereich ist.
Chris
9

Ich denke, was Sie erreichen wollen, kann einfach so gemacht werden:

Verwenden Sie beim Kompilieren des Kaffeeskripts den Parameter "-b".

-b/ --bare Kompilieren Sie das JavaScript ohne den Funktionssicherheits-Wrapper der obersten Ebene.

Also so etwas wie das: coffee -b --compile somefile.coffee whatever.js

Dadurch wird Ihr Code genau wie auf der Website CoffeeScript.org ausgegeben.

Sankalp Singha
quelle
7

Wenn Sie ein schlechter Mensch sind (ich bin ein schlechter Mensch), können Sie so einfach werden: (->@)()

Wie in,

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

Dies funktioniert, da beim Aufrufen von a Referencezu einem Function'bare' (dh func()anstelle von new func()oder obj.func()) etwas, das üblicherweise als 'Funktionsaufruf-Aufrufmuster' bezeichnet wird, immer an thisdas globale Objekt für diesen Ausführungskontext gebunden ist .

Das obige CoffeeScript kompiliert einfach zu (function(){ return this })(); Wir üben dieses Verhalten aus, um zuverlässig auf das globale Objekt zuzugreifen.

ELLIOTTCABLE
quelle
Das ist brilliant!
Metalim
Das einzige was bei mir funktioniert hat. Hasse CoffeeScript.
PCV
Liebe CoffeeScript. Bisher ist es die beste Programmiersprache da draußen. Schade, dass es als Hobbyprojekt erstellt und gepflegt wurde, was zu Chaos und Dummheit in seinen Nutzungsmustern führte.
Metalim
3

Da Coffeescript nur selten alleine verwendet wird, können Sie globalVariablen verwenden, die entweder von node.js oder browserify (und von Nachkommen wie Coffeeify, Gulp Build-Skripten usw.) bereitgestellt werden.

In node.js globalbefindet sich der globale Namespace.

In browserify globalist gleich window.

Also nur:

somefunc = ->
  global.variable = 123
metalim
quelle