Come definisco le variabili globali in CoffeeScript?


317

Su Coffeescript.org:

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

bawbag(5, 10) 

compilerebbe per:

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

la compilazione tramite coffee-script in node.js lo avvolge così:

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

I documenti dicono:

Se desideri creare variabili di livello superiore da utilizzare per altri script, allegarle come proprietà sulla finestra o sull'oggetto export in CommonJS. L'operatore esistenziale (trattato di seguito) ti offre un modo affidabile per capire dove aggiungerli, se hai come target sia CommonJS che il browser: root = export? Questo

Come definisco le variabili globali quindi in CoffeeScript. Che cosa significa "allegarli come proprietà sulla finestra"?


4
Si noti che l'utilizzo delle variabili globali è negativo, c2.com/cgi/wiki?GlobalVariablesAreBad e persino considerato dannoso, c2.com/cgi/wiki?GotoConsideredHarmful . E non c'è davvero alcun motivo per usarli in JavaScript, dato che hai grandi funzionalità come le chiusure che possono risolvere la maggior parte dei problemi che stai usando per risolvere le variabili globali.
Evgeny,

9
@Evgeny Mentre sono d'accordo con te qui, in alcuni casi è necessario creare un oggetto 'app' centrale e avere moduli collegati ad esso.
jackyalcine,

1
gli oggetti centrali possono essere salvati in oggetti di stato globale esistenti, come l' windowoggetto o l' exportsoggetto. non è necessario creare variabili globali.
Evgeny,

9
Le variabili globali di @Evgeny vengono salvate come proprietà dell'oggetto window(o globalsu nodejs)
shesek

21
Sì, non è un male avere una var globale. Solo una cattiva pratica per appiattire la tua app senza pensarci. Dichiararne uno e usarlo come una fabbrica di adattatori come jQuery o una specie di spazio dei nomi è una pratica molto comune.
Erik Reppen,

Risposte:


419

Poiché lo script coffee non ha alcuna varistruzione, lo inserisce automaticamente per tutte le variabili nello script coffee, in questo modo impedisce alla versione compilata di JavaScript di perdere tutto nello spazio dei nomi globale .

Quindi, poiché non c'è modo di far "fuoriuscire" qualcosa nello spazio dei nomi globale dal lato dello script del caffè delle cose di proposito, è necessario definire le variabili globali come proprietà dell'oggetto globale .

allegarli come proprietà sulla finestra

Ciò significa che devi fare qualcosa di simile window.foo = 'baz';, che gestisce il caso del browser, poiché lì l' oggetto globale è il window.

Node.js

In Node.js non c'è nessun windowoggetto, invece c'è l' exportsoggetto che viene passato nel wrapper che avvolge il modulo Node.js (Vedi: https://github.com/ry/node/blob/master/src/node.js# L321 ), quindi in Node.js quello che dovresti fare è exports.foo = 'baz';.

Ora diamo un'occhiata a ciò che afferma nella tua citazione dai documenti:

... indirizzato sia a CommonJS che al browser: root = export? Questo

Questo è ovviamente un copione del caffè, quindi diamo un'occhiata a ciò che effettivamente compila:

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

Innanzitutto verificherà se exportsè definito, poiché il tentativo di fare riferimento a una variabile inesistente in JavaScript altrimenti produrrebbe un SyntaxError (tranne quando viene utilizzato con typeof)

Quindi, se exportsesiste, come nel caso di Node.js (o in un sito Web mal scritto ...), punterà a exports, altrimenti a this. Allora che cos'è this?

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

L'uso di .calluna funzione vincolerà l' thisinterno della funzione al primo parametro passato, nel caso in cui il browser thisora sia l' windowoggetto, nel caso di Node.js sarebbe il contesto globale che è anche disponibile come globaloggetto.

Ma poiché hai la requirefunzione in Node.js, non è necessario assegnare qualcosa globalall'oggetto in Node.js, invece devi assegnare exportsall'oggetto che poi viene restituito dalla requirefunzione.

Coffee-Script

Dopo tutta quella spiegazione, ecco cosa devi fare:

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

Questo dichiarerà la nostra funzione foonello spazio dei nomi globale (qualunque cosa accada).
È tutto :)


1
@IvoWetzel - Qual è la differenza tra i global, GLOBALe rootoggetti in node.js?
Aadit M Shah,

1
il tentativo di fare riferimento a una variabile inesistente in JavaScript altrimenti produrrebbe un errore di sintassi Non vuoi dire ReferenceError?
alex,

12
O ancora più breve:(exports ? this).foo = -> 'Hello World'
Dane O'Connor il

3
this.foo è spesso! = window.foo anche se se sei "questo" contesto è già un oggetto. Questa è una sintassi confusa.
Kevin,

1
Mentre sono d'accordo con l'utilizzo global = exports ? this. L'affermazione che "nel caso di Node.js sarebbe il contesto globale ..." è errata perché la thisvariabile, quando richiesta o eseguita da node.js, viene valutata come ambito del modulo. Quindi, se prevedi di impostarlo, lo renderai accessibile a livello globale, rimarrai deluso. Se si desidera impostare le cose a livello globale nel contesto node.js, è necessario utilizzare la globalvariabile anziché this.
KFL

58

A me sembra che @atomicules abbia la risposta più semplice, ma penso che possa essere semplificato un po 'di più. Devi mettere @prima di tutto ciò che vuoi essere globale, in modo che si compili this.anythinge si thisriferisca all'oggetto globale.

così...

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

bawbag(5, 10)

compila in ...

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

e funziona all'interno e all'esterno del wrapper fornito da 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
Ma questo non funzionerà se sei già dentro un altro ambito giusto? Perché allora thisnon si riferisce più all'oggetto globale
Sherwin Yu

1
È corretto, quindi puoi definire la tua variabile in un ambito appropriato (e usarla in altri), oppure definire come window.myVariablefunzionerà ovunque.
Billy Moon,

2
Non è necessario definire un'altra variabile, basta usare =>invece ->che indica a coffeescript di creare la funzione nello spazio dei nomi this / global
Ricardo Villamil,

2
questo è stato così utile, ora posso creare oggetti e funzioni globali in una sceneggiatura del caffè separata
Diego Fernando Murillo Valenci,

È molto meglio Il trasferimento di JS in CS mi ha richiesto di modificare molte chiamate di funzione per utilizzare l'oggetto finestra, ora posso ripristinarlo
casraf

33

Ivo l'ha inchiodato, ma dirò che c'è un trucco sporco che puoi usare, anche se non lo consiglio se stai cercando punti di stile: puoi incorporare il codice JavaScript direttamente nel tuo CoffeeScript sfuggendolo con i backtick.

Tuttavia, ecco perché questa è di solito una cattiva idea: il compilatore CoffeeScript non è a conoscenza di tali variabili, il che significa che non obbediranno alle normali regole di scoping di CoffeeScript. Così,

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

compila a

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

e ora hai due foos in diversi ambiti. Non c'è modo di modificare il globale foo dal codice CoffeeScript senza fare riferimento all'oggetto globale, come descritto da Ivy.

Ovviamente, questo è un problema solo se si fooesegue un'assegnazione in CoffeeScript, se si foodiventa di sola lettura dopo aver ricevuto il valore iniziale (ovvero una costante globale), l'approccio della soluzione JavaScript integrata potrebbe essere in qualche modo accettabile (anche se comunque non consigliato).


1
Questa è stata una soluzione utile per me poiché sto usando Titanium con CoffeeScript. Le esportazioni e l'oggetto finestra sono inesistenti lì.
Pier-Olivier Thibault,

In realtà questa è solo una foovariabile locale , a causa del varsollevamento (JS esegue la scansione di tutte le vardichiarazioni e le interpreta come se fossero al top della funzione)
Kornel

@porneL Hai ragione; Ho scelto un cattivo esempio. Il punto è che il compilatore CoffeeScript non esegue alcuna analisi di JavaScript con escape backtick, quindi è possibile ottenere output dispari.
Trevor Burnham,

2
@ Pier-OlivierThibault Se vuoi usare Globals in Titanium puoi usare Ti.App.myGlobalVar = "ImAGlobalVar" e non hai bisogno di
backtick

questa è la risposta corretta, almeno per Node.js. fare expect = require('chai').expect;faexpect variabile disponibile in tutti i miei file di test!
pocesar,

11

Puoi passare l'opzione -b quando compili il codice tramite coffee-script in node.js. Il codice compilato sarà lo stesso di coffeescript.org.


Come? Dove metto l'opzione -b?
Harry,

1
@Harry - -b/ --bareva direttamente dopo il coffeecomando.
ocodo,

9

Aggiungere a risposta di Ivo Wetzel

Sembra esserci una sintassi abbreviata per exports ? thisquella che posso trovare solo documentata / menzionata su un post di un gruppo di Google .

Cioè in una pagina web per rendere disponibile una funzione a livello globale, si dichiara nuovamente la funzione con un @prefisso:

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

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

9
'@' In @aglobalfunction viene semplicemente sostituito da 'this.', Quindi compilando in 'this.aglobalfunction'. Questo funziona perché l'ambito della funzione wrapper coffeescript (se applicato) è l'ambito globale.
Chris,

9

Penso che ciò che stai cercando di ottenere possa essere fatto semplicemente in questo modo:

Durante la compilazione del coffeescript, utilizzare il parametro "-b".

-b/ --bare Compilare JavaScript senza il wrapper di sicurezza della funzione di livello superiore.

Quindi qualcosa del genere: coffee -b --compile somefile.coffee whatever.js

Questo genererà il tuo codice proprio come nel sito CoffeeScript.org.


7

Se sei una persona cattiva (io sono una persona cattiva.), Puoi diventare semplice come questo: (->@)()

Come in,

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

Questo funziona, perché quando si invoca a Referencea Function'nudo' (ovvero func(), invece di new func()o obj.func()), qualcosa comunemente indicato come 'modello di richiamo della funzione-chiamata', si lega semprethis all'oggetto globale per quel contesto di esecuzione .

Il CoffeeScript sopra semplicemente si compila in (function(){ return this })(); quindi stiamo esercitando quel comportamento per accedere in modo affidabile all'oggetto globale.


È brillante!
metalim,

L'unica cosa che ha funzionato per me. Hate CoffeeScript.
pcv

Love CoffeeScript. Finora è il miglior linguaggio di programmazione là fuori. Peccato che sia stato creato e mantenuto come un progetto hobby, portando a caos e stupidità nei suoi schemi di utilizzo.
metalim,

3

Poiché il coffeescript viene usato raramente da solo, è possibile utilizzare global variabile fornita da node.js o browserify (e qualsiasi discendente come coffeeify, gulp build script, ecc.).

In node.js global è lo spazio dei nomi globale.

In browserify globalè uguale awindow .

Quindi:

somefunc = ->
  global.variable = 123
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.