¿Cómo defino variables globales en CoffeeScript?

317

En Coffeescript.org:

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

bawbag(5, 10) 

compilaría para:

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

compilando a través de coffee-script en node.js envuelve eso:

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

Los documentos dicen:

Si desea crear variables de nivel superior para que otras secuencias de comandos las utilicen, adjúntelas como propiedades en la ventana o en el objeto de exportaciones en CommonJS. El operador existencial (cubierto a continuación), le brinda una forma confiable de averiguar dónde agregarlos, si está apuntando tanto a CommonJS como al navegador: raíz = exportaciones? esta

¿Cómo defino las variables globales en CoffeeScript? ¿Qué significa 'adjuntarlos como propiedades en la ventana'?

Tejedor manual
fuente
44
Tenga en cuenta que el uso de variables globales es malo, c2.com/cgi/wiki?GlobalVariablesAreBad , e incluso se considera dañino, c2.com/cgi/wiki?GotoConsideredHarmful . Y realmente no hay ninguna razón para usarlos en JavaScript, ya que tiene excelentes características como cierres que pueden resolver la mayoría de los problemas que está utilizando para resolver las variables globales.
Evgeny
99
@Evgeny Aunque estoy de acuerdo con usted aquí, en algunos casos es necesario crear un objeto central de 'aplicación' y tener módulos adjuntos.
jackyalcine
1
Los objetos centrales se pueden guardar en objetos de estado global existentes, como el windowobjeto o el exportsobjeto. No es necesario crear variables globales.
Evgeny
99
@Evgeny las variables globales se guardan como propiedades del objeto window(o globalen nodejs)
shesek
21
Sí, no es malo tener una var global. Solo es una mala práctica cargar tu aplicación con ellos sin pensar. Declarar uno y usarlo como una fábrica de adaptadores como jQuery o algún tipo de espacio de nombres es una práctica muy común.
Erik Reppen

Respuestas:

419

Dado que la secuencia de comandos de café no tiene ninguna vardeclaración, la inserta automáticamente para todas las variables en la secuencia de comandos de café, de esa manera evita que la versión compilada de JavaScript filtre todo en el espacio de nombres global .

Entonces, dado que no hay forma de hacer que algo "se filtre" en el espacio de nombres global desde el lado del script de café de las cosas a propósito, debe definir sus variables globales como propiedades del objeto global .

adjuntarlos como propiedades en la ventana

Esto significa que debe hacer algo como window.foo = 'baz';manejar el caso del navegador, ya que allí el objeto global es el window.

Node.js

En Node.js no hay ningún windowobjeto, en su lugar está el exportsobjeto que se pasa al contenedor que envuelve el módulo Node.js (Ver: https://github.com/ry/node/blob/master/src/node.js# L321 ), entonces en Node.js lo que necesitarías hacer es exports.foo = 'baz';.

Ahora echemos un vistazo a lo que dice en su cita de los documentos:

... dirigido tanto a CommonJS como al navegador: raíz = exportaciones? esta

Esto es obviamente un script de café, así que echemos un vistazo a lo que esto realmente compila:

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

Primero verificará si exportsestá definido, ya que intentar hacer referencia a una variable inexistente en JavaScript produciría un SyntaxError (excepto cuando se usa con typeof)

Entonces, si exportsexiste, que es el caso en Node.js (o en un sitio web mal escrito ...), la raíz apuntará exports, de lo contrario, a this. Entonces que es this?

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

El uso .callde una función vinculará el thisinterior de la función con el primer parámetro pasado, en el caso de que el navegador thisahora sea el windowobjeto, en el caso de Node.js sería el contexto global que también está disponible como el globalobjeto.

Pero dado que tiene la requirefunción en Node.js, no es necesario asignar algo al globalobjeto en Node.js, sino que asigna al exportsobjeto que luego devuelve la requirefunción.

Coffee-Script

Después de toda esa explicación, esto es lo que debe hacer:

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

Esto declarará nuestra función fooen el espacio de nombres global (sea lo que sea).
Eso es todo :)

Ivo Wetzel
fuente
1
@IvoWetzel - ¿Cuál es la diferencia entre los global, GLOBALy rootlos objetos en Node.js?
Aadit M Shah
1
tratar de hacer referencia a una variable no existente en JavaScript produciría un SyntaxError ¿No quieres decir ReferenceError?
alex
12
O incluso más corto:(exports ? this).foo = -> 'Hello World'
Dane O'Connor
3
this.foo es a menudo! = window.foo aunque si eres 'este' contexto ya es un objeto. Esta es una sintaxis confusa.
Kevin
1
Si bien estoy de acuerdo con el uso global = exports ? this. La afirmación de que "en el caso de Node.js sería el contexto global ..." es incorrecta porque la thisvariable, cuando es requerida o ejecutada por node.js, se evalúa como el alcance del módulo. Entonces, si espera establecer accesorios para que sea accesible a nivel mundial, se sentirá decepcionado. Si desea establecer las cosas globalmente en el contexto de node.js, debe usar la globalvariable, en lugar de hacerlo this.
KFL
58

Para mí, parece que @atomicules tiene la respuesta más simple, pero creo que se puede simplificar un poco más. Debe poner un @antes de cualquier cosa que quiera que sea global, para que se compile this.anythingy haga thisreferencia al objeto global.

entonces...

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

bawbag(5, 10)

compila para ...

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

y funciona dentro y fuera del contenedor dado por 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
Billy Moon
fuente
77
Pero esto no funcionará si ya estás dentro de otro alcance, ¿verdad? Porque entonces thisya no se refiere al objeto global
Sherwin Yu
1
Eso es correcto, por lo que puede definir su variable en un ámbito apropiado (y usarla en otros), o definir window.myVariablecuál funcionará en cualquier lugar.
Billy Moon
2
No necesita definir otra variable, solo use en =>lugar de ->que instruya coffeescript para crear la función bajo el espacio de nombre this / global
Ricardo Villamil
2
Esto fue muy útil, ahora puedo crear objetos y funciones globales en un script de café separado
Diego Fernando Murillo Valenci el
Esto es mucho mejor Transferir JS a CS necesitaba que cambiara muchas llamadas de función para usar el objeto de ventana, ahora puedo revertir eso
casraf
33

Ivo lo acertó, pero mencionaré que hay un truco sucio que puedes usar, aunque no lo recomiendo si buscas puntos de estilo: puedes incrustar código JavaScript directamente en tu CoffeeScript escapándolo con backticks.

Sin embargo, he aquí por qué suele ser una mala idea: el compilador de CoffeeScript desconoce esas variables, lo que significa que no obedecerán las reglas de alcance normales de CoffeeScript. Entonces,

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

compila a

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

y ahora tienes dos foos en diferentes ámbitos. No hay forma de modificar el código global foo de CoffeeScript sin hacer referencia al objeto global, como describió Ivy.

Por supuesto, esto es solo un problema si realiza una asignación fooen CoffeeScript: si se fooconvierte en solo lectura después de recibir su valor inicial (es decir, es una constante global), entonces el enfoque de la solución de JavaScript incrustado podría ser algo aceptable (aunque aún así no recomendado).

Trevor Burnham
fuente
1
Esta fue una solución útil para mí ya que estoy usando Titanium con CoffeeScript. Las exportaciones y el objeto de la ventana son inexistentes allí.
Pier-Olivier Thibault
En realidad, esa es solo una foovariable local , debido a la varelevación (JS escanea todas las vardeclaraciones y las interpreta como si estuvieran en la parte superior de la función)
Kornel
@porneL Tienes razón; Elegí un mal ejemplo. El punto es que el compilador CoffeeScript no realiza ningún análisis de JavaScript escapado por retroceso, por lo que puede obtener resultados extraños.
Trevor Burnham
2
@ Pier-OlivierThibault Si desea usar Globals en Titanium puede usar Ti.App.myGlobalVar = "ImAGlobalVar" y no necesita backticks
Jakob Lnr
Esta es la respuesta correcta, al menos para Node.js. haciendo expect = require('chai').expect;marcas expectdisponibles variable en todos mis archivos de prueba!
pocesar
11

Puede pasar la opción -b cuando compila código mediante coffee-script en node.js. El código compilado será el mismo que en coffeescript.org.

phongnh
fuente
¿Cómo? ¿Dónde pongo la opción -b?
Harry
1
@Harry - -b/ --bareva directamente después del coffeecomando.
ocodo
9

Para agregar a la respuesta de Ivo Wetzel

Parece que hay una sintaxis abreviada para exports ? thiseso que solo puedo encontrar documentado / mencionado en una publicación grupal de Google .

Es decir, en una página web para que una función esté disponible globalmente, declara la función nuevamente con un @prefijo:

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

<a href="javascript:aglobalfunction()" >Click me!</a>
atomicules
fuente
99
La '@' en @aglobalfunction simplemente se reemplaza por 'this.', Compilando así a 'this.aglobalfunction'. Esto funciona porque el alcance de la función de contenedor coffeescript (si se aplica) es el alcance global.
Chris
9

Creo que lo que estás tratando de lograr simplemente se puede hacer así:

Mientras compila el coffeescript, use el parámetro "-b".

-b/ --bare Compile el JavaScript sin la función de seguridad de nivel superior.

Entonces algo como esto: coffee -b --compile somefile.coffee whatever.js

Esto generará su código al igual que en el sitio CoffeeScript.org.

Sankalp Singha
fuente
7

Si eres una mala persona (yo soy una mala persona), puedes ser tan simple como esto: (->@)()

Como en,

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

Esto funciona, porque cuando se invoca a Referencea Function'bare' (es decir, en func()lugar de new func()o obj.func()), algo comúnmente conocido como 'patrón de invocación de llamada a función', siempre se une thisal objeto global para ese contexto de ejecución .

El CoffeeScript anterior simplemente se compila a (function(){ return this })(); entonces estamos ejerciendo ese comportamiento para acceder de manera confiable al objeto global.

ELLIOTTCABLE
fuente
¡Esto es brillante!
metalim
Lo único que funcionó para mí. Odio CoffeeScript.
pcv
Amor CoffeeScript. Hasta ahora es el mejor lenguaje de programación que existe. Lástima que fue creado y mantenido como un proyecto de pasatiempo, lo que lleva al caos y la estupidez en sus patrones de uso.
metalim
3

Dado que coffeescript rara vez se usa solo, puede usar la globalvariable proporcionada por node.js o browserify (y cualquier descendiente como coffeeify, scripts de compilación gulp, etc.).

En node.js globalhay un espacio de nombres global.

En browserify globales igual a window.

Por lo que sólo:

somefunc = ->
  global.variable = 123
metalim
fuente