Como defino variáveis ​​globais no CoffeeScript?

317

No Coffeescript.org:

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

bawbag(5, 10) 

compilaria para:

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

compilar via coffee-script em node.js envolve o seguinte:

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

Os documentos dizem:

Se você deseja criar variáveis ​​de nível superior para uso de outros scripts, anexe-as como propriedades na janela ou no objeto de exportação no CommonJS. O operador existencial (abordado abaixo) fornece uma maneira confiável de descobrir onde adicioná-los, se você estiver direcionando o CommonJS e o navegador: root = export? isto

Como defino Variáveis ​​Globais, em seguida, no CoffeeScript. O que significa 'anexá-los como propriedades na janela'?

Handloomweaver
fonte
4
Observe que o uso de variáveis ​​globais é ruim, c2.com/cgi/wiki?GlobalVariablesAreBad , e até considerado prejudicial, c2.com/cgi/wiki?GotoConsideredHarmful . E não há realmente nenhuma razão para usá-los em JavaScript, pois você possui ótimos recursos, como fechamentos que podem resolver a maioria dos problemas que estão usando variáveis ​​globais para resolver.
Evgeny
9
@Evgeny Embora eu concorde com você aqui, em alguns casos é necessário criar um objeto 'app' central e ter módulos anexados a ele.
22412 jackyalcine
1
objetos centrais podem ser salvos em objetos de estado global existentes, como o windowobjeto ou o exportsobjeto. não há necessidade de criar variáveis ​​globais.
Evgeny
9
@Evgeny variáveis globais são salvos como propriedades do window(ou globalobjeto na nodejs)
shesek
21
Sim, não é ruim ter uma var global. Apenas uma prática ruim para reduzir seu aplicativo com eles sem pensar. Declarar um e usá-lo como uma fábrica de adaptadores como o jQuery ou algum tipo de espaço para nome é uma prática realmente comum.
Erik Reppen

Respostas:

419

Como o script coffee não possui nenhuma varinstrução, ele o insere automaticamente para todas as variáveis ​​no script coffee-coffee, dessa forma evita que a versão JavaScript compilada vaze tudo para o diretório espaço de nomes global .

Portanto, como não há como "vazar" algo no espaço para nome global a partir do lado do script de café, você precisa definir suas variáveis ​​globais como propriedades do objeto global .

anexá-los como propriedades na janela

Isso significa que você precisa fazer algo como window.foo = 'baz';, que lida com o caso do navegador, já que existe o objeto global é o window.

Node.js

No Node.js, não há windowobjeto, mas o exportsobjeto que é passado para o wrapper que envolve o módulo Node.js. (Veja: https://github.com/ry/node/blob/master/src/node.js# L321 ), então no Node.js o que você precisa fazer é exports.foo = 'baz';.

Agora, vamos dar uma olhada no que ele diz em sua citação dos documentos:

... visando o CommonJS e o navegador: root = exportações? isto

Obviamente, isso é um script de café, então vamos dar uma olhada no que isso realmente compila:

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

Primeiro, ele verificará se exports está definido, pois tentar fazer referência a uma variável inexistente no JavaScript produziria um SyntaxError (exceto quando usado com typeof)

Portanto, se exportsexistir, como é o caso do Node.js (ou em um site mal gravado ...), a raiz apontará para o exportscontrário this. Então o que é this?

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

O uso .callde uma função vinculará a thisfunção interna ao primeiro parâmetro passado, no caso do navegador thisagora ser o windowobjeto; no caso do Node.js, seria o contexto global que também está disponível como globalobjeto.

Mas como você tem a requirefunção no Node.js., não há necessidade de atribuir algo ao globalobjeto no Node.js. Em vez disso, você o atribui ao exportsobjeto que é retornado pela requirefunção.

Coffee-Script

Depois de toda essa explicação, aqui está o que você precisa fazer:

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

Isso declarará nossa função foono espaço de nomes global (o que quer que seja).
Isso é tudo :)

Ivo Wetzel
fonte
1
@IvoWetzel - Qual é a diferença entre as global, GLOBALe rootobjetos em node.js?
Aadit M Shah 02/12/11
1
tentar referenciar uma variável inexistente no JavaScript produziria um SyntaxError Você não quer dizer ReferenceError?
Alex
12
Ou ainda mais curto:(exports ? this).foo = -> 'Hello World'
Dane O'Connor
3
this.foo é frequentemente! = window.foo embora se você é 'este' contexto já é um objeto. Essa é uma sintaxe confusa.
21712 Kevin
1
Enquanto eu concordo com o uso global = exports ? this. A afirmação de que "no caso de Node.js seria o contexto global ..." está incorreta porque a thisvariável, quando requerida ou executada por node.js, é avaliada como o escopo do módulo. Portanto, se você espera que definir adereços o torne acessível globalmente, ficará desapontado. Se você deseja definir as coisas globalmente no contexto node.js., você precisa usar a globalvariável, em vez de this.
KFL
58

Para mim, parece que @atomicules tem a resposta mais simples, mas acho que pode ser simplificada um pouco mais. Você precisa colocar um @antes de qualquer coisa que queira que seja global, para que seja compilado this.anythinge thisse refira ao objeto global.

tão...

@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);

e funciona dentro e fora do wrapper fornecido pelo 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
fonte
7
Mas isso não funcionará se você já estiver em outro escopo, certo? Porque, então, thisnão se refere ao objeto global
Sherwin Yu
1
Isso está correto, então você pode definir sua variável em um escopo apropriado (e usá-la em outros) ou definir como a window.myVariableque funcionará em qualquer lugar.
Billy Lua
2
Você não precisa definir uma outra variável é só usar =>em vez de ->que instrui Coffeescript para criar a função sob a esse namespace / global
Ricardo Villamil
2
este foi tão útil, agora eu posso criar objetos e funções globais em um script de café em separado
Diego Fernando Murillo Valenci
Isto é muito melhor. A transferência de JS para CS precisou que eu alterasse muitas chamadas de função para usar o objeto window, agora posso reverter isso #
casraf
33

O Ivo acertou em cheio, mas vou mencionar que há um truque sujo que você pode usar, embora eu não o recomende se você estiver procurando por pontos de estilo: você pode incorporar o código JavaScript diretamente no seu CoffeeScript, escapando-o com retalhos.

No entanto, eis o motivo pelo qual essa geralmente é uma má idéia: O compilador CoffeeScript não tem conhecimento dessas variáveis, o que significa que elas não obedecerão às regras normais de escopo do CoffeeScript. Assim,

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

compila para

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

e agora você tem dois foosegundos em escopos diferentes. Não há como modificar o global foo do código CoffeeScript sem fazer referência ao objeto global, como Ivy descreveu.

Obviamente, isso é apenas um problema se você fizer uma atribuição foono CoffeeScript - se se footornar somente leitura depois de receber seu valor inicial (ou seja, é uma constante global), a abordagem da solução JavaScript incorporada poderá ser meio aceitável (embora ainda seja não recomendado).

Trevor Burnham
fonte
1
Esta foi uma solução útil para mim, pois estou usando o Titanium com CoffeeScript. Exportações e objetos de janela são inexistentes lá.
Pier-Olivier Thibault
Na verdade isso é apenas um local de foovariável, por causa da varelevação (JS verifica frente para todas as vardeclarações e interpreta-os como se eles estavam no topo da função)
Kornel
@porneL Você está correto; Eu escolhi um mau exemplo. O ponto é que o compilador CoffeeScript não realiza nenhuma análise do JavaScript com escape de backtick, para que você possa obter resultados ímpares.
Trevor Burnham
2
@ Pier-OlivierThibault Se você quiser usar Globals em Titanium você pode usar Ti.App.myGlobalVar = "ImAGlobalVar" e NÃO FAZEM backticks necessidade
Jakob LNR
esta é a resposta correta, pelo menos para o Node.js. doing expect = require('chai').expect;torna a expectvariável disponível em todos os meus arquivos de teste!
Pocesar
11

Você pode passar a opção -b ao compilar o código via coffee-script em node.js. O código compilado será o mesmo que no coffeescript.org.

phongnh
fonte
Quão? Onde coloco a opção -b?
Harry
1
@Harry - -b/ --barevai diretamente após o coffeecomando.
Ocho
9

Para adicionar à resposta de Ivo Wetzel

Parece haver uma sintaxe abreviada para a exports ? thisqual só consigo encontrar documentado / mencionado em uma postagem do grupo do Google .

Ou seja, em uma página da web para disponibilizar uma função globalmente, você declara a função novamente com um @prefixo:

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

<a href="javascript:aglobalfunction()" >Click me!</a>
átomos
fonte
9
O '@' na função @aglobal é simplesmente substituído por 'this.', Então compilando para 'this.aglobalfunction'. Isso funciona porque o escopo da função de wrapper coffeescript (se aplicado) é o escopo global.
Chris
9

Eu acho que o que você está tentando alcançar pode ser feito simplesmente assim:

Enquanto você estiver compilando o coffeescript, use o parâmetro "-b".

-b/ --bare Compile o JavaScript sem o wrapper de segurança da função de nível superior.

Então, algo como isto: coffee -b --compile somefile.coffee whatever.js

Isso produzirá seu código exatamente como no site CoffeeScript.org.

Sankalp Singha
fonte
7

Se você é uma pessoa má (eu sou uma pessoa má.), Você pode ser simples assim: (->@)()

Como em,

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

Isso funciona porque, ao chamar a Referencepara Function'bare' (ou seja, em func()vez de new func()ou obj.func()), algo geralmente chamado de 'padrão de chamada de função', sempre se liga thisao objeto global para esse contexto de execução .

O CoffeeScript acima simplesmente compila para (function(){ return this })(); portanto, estamos exercitando esse comportamento para acessar de maneira confiável o objeto global.

ELLIOTTCABLE
fonte
Isto é brilhante!
metalim
A única coisa que funcionou para mim. Odeio o CoffeeScript.
PCV
Amo CoffeeScript. Até agora, é a melhor linguagem de programação existente. Pena que foi criado e mantido como um projeto de hobby, levando ao caos e estupidez em seus padrões de uso.
metalim
3

Como o coffeescript raramente é usado por si só, você pode usar a globalvariável fornecida pelo node.js ou pelo browserify (e quaisquer descendentes como coffeeify, scripts de construção do gulp, etc.).

No node.js globalestá o espaço para nome global.

No browserify globalé igual a window.

Então apenas:

somefunc = ->
  global.variable = 123
metalim
fonte