Как определить глобальные переменные в 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);

компиляция с помощью coffee-script под node.js оборачивает это так:

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

Документы говорят:

Если вы хотите создать переменные верхнего уровня для использования другими сценариями, прикрепите их как свойства к окну или к объекту экспорта в CommonJS. Экзистенциальный оператор (описанный ниже) дает вам надежный способ выяснить, куда их добавить, если вы ориентируетесь и на CommonJS, и на браузер: root = exports? этот

Как определить глобальные переменные тогда в CoffeeScript. Что означает «прикрепить их как свойства к окну»?

Handloomweaver
источник
4
Обратите внимание, что использование глобальных переменных - это плохо, c2.com/cgi/wiki?GlobalVariablesAreBad и даже считается вредным, c2.com/cgi/wiki?GotoConsideredHarmful . И на самом деле нет никакой причины использовать их в JavaScript вообще, так как у вас есть отличные функции, такие как замыкания, которые могут решить большинство проблем, которые вы используете для решения глобальных переменных.
Евгений
9
@Evgeny Хотя я согласен с вами здесь, в некоторых случаях необходимо создать центральный объект «приложение» и иметь к нему модули.
Jackyalcine
1
центральные объекты могут быть сохранены в существующие объекты глобального состояния, такие как windowобъект или exportsобъект. нет необходимости создавать глобальные переменные.
Евгений
9
@Evgeny глобальные переменные сохраняются как свойства объекта window(или globalна nodejs)
shesek
21
Да, не так уж плохо иметь глобальную переменную. Просто плохая практика, чтобы бездумно связывать ваше приложение с ними. Объявление одного и использование его в качестве фабрики адаптеров, такого как jQuery или некоторого пространства имен, - это действительно распространенная практика.
Эрик Реппен

Ответы:

419

Поскольку у сценария кофе нет varоператоров, он автоматически вставляет его для всех переменных в сценарии кофе, таким образом он предотвращает утечку скомпилированной версии 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 = export? этот

Это, очевидно, сценарий кофе, так что давайте посмотрим, к чему это на самом деле компилируется:

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

Сначала он проверит, exportsопределено ли это, поскольку попытка ссылки на несуществующую переменную в JavaScript в противном случае приведет к ошибке SyntaxError (кроме случаев, когда она используется с typeof)

Таким образом, если exportsсуществует, что имеет место в корне Node.js (или в плохо написанном веб-сайте ...), на него будет указывать корень, в exportsпротивном случае - на this. Ну и что this?

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

Использование .callфункции on будет связывать thisвнутреннюю функцию с первым передаваемым параметром, в случае, если браузер thisтеперь будет windowобъектом, в случае Node.js это будет глобальный контекст, который также доступен как globalобъект.

Но поскольку у вас есть requireфункция в Node.js, нет необходимости назначать что-либо globalобъекту в Node.js, вместо этого вы присваиваете exportsобъект, который затем возвращается requireфункцией.

Кофе-Script

После всего этого объяснения вот что вам нужно сделать:

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

Это объявит нашу функцию fooв глобальном пространстве имен (что бы это ни было).
Вот и все :)

Иво Ветцель
источник
1
@IvoWetzel - В чем разница между global, GLOBALи rootобъектов в Node.js?
Аадит М Шах
1
попытка ссылки на несуществующую переменную в JavaScript в противном случае привела бы к SyntaxError. Разве вы не имеете в виду ReferenceError?
Алекс
12
Или даже короче:(exports ? this).foo = -> 'Hello World'
Дейн О'Коннор
3
this.foo - это часто! = window.foo, хотя, если вы «this», контекст уже является объектом. Это запутанный синтаксис.
Кевин
1
Пока я согласен с использованием global = exports ? this. Утверждение, что «в случае Node.js это будет глобальный контекст ...» неверно, потому что thisпеременная, когда требуется или запускается node.js, оценивается как область действия модуля. Так что, если вы ожидаете, что установка реквизитов сделает его доступным во всем мире, вы будете разочарованы. Если вы хотите установить все глобально в контексте node.js, вам нужно использовать globalпеременную, а не this.
KFL
58

Мне кажется, @atomicules имеет самый простой ответ, но я думаю, что его можно упростить немного больше. Вам нужно поставить @перед всем, что вы хотите быть глобальным, чтобы он компилировался this.anythingи thisссылался на глобальный объект.

так...

@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больше не ссылается на глобальный объект
Шервин Ю
1
Это правильно, поэтому вы можете либо определить свою переменную в соответствующей области (и использовать ее в других), либо определить, window.myVariableкакая из них будет работать где угодно.
Билли Мун
2
Вам не нужно определять другую переменную, просто используйте =>вместо ->этого команду coffeescript для создания функции в пространстве имен this / global
Ricardo Villamil
2
это было очень полезно, теперь я могу создавать глобальные объекты и функции в отдельном кофейном скрипте
Диего Фернандо Мурильо Валенси
Это намного лучше. Передача JS в CS потребовала, чтобы я изменил множество вызовов функций для использования объекта окна, теперь я могу это отменить
casraf
33

Ivo прибил его, но я упомяну, что есть один грязный трюк, который вы можете использовать, хотя я не рекомендую его, если вы собираетесь использовать стилевые точки: вы можете встраивать код JavaScript прямо в ваш CoffeeScript, избегая его с помощью обратных кавычек.

Однако, вот почему это обычно плохая идея: компилятор CoffeeScript не знает об этих переменных, что означает, что они не будут подчиняться нормальным правилам области действия CoffeeScript. Так,

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

компилируется в

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

и теперь у вас есть две fooпары в разных сферах. Как описал Айви , невозможно изменить глобальный foo код CoffeeScript без ссылки на глобальный объект.

Конечно, это проблема, только если вы сделаете присваивание fooв CoffeeScript - если оно fooстало доступным только для чтения после получения его начального значения (т. Е. Глобальной константы), тогда подход к встроенному решению JavaScript может быть своего рода приемлемым (хотя все же не рекомендуется).

Тревор Бернхэм
источник
1
Это было для меня полезным решением, так как я использую Titanium с CoffeeScript. Экспорт и оконный объект там не существуют.
Пьер-Оливье Тибо
На самом деле это всего лишь одна локальная fooпеременная из-за varподъема (JS просматривает все varобъявления вперед и интерпретирует их, как если бы они были в верхней части функции)
Kornel
@porneL Вы правы; Я выбрал плохой пример. Дело в том, что компилятор CoffeeScript не выполняет анализ JavaScript с обратной косой чертой, поэтому вы можете получить странный вывод.
Тревор Бернхам
2
@ Pier-OlivierThibault Если вы хотите использовать Globals в Titanium, вы можете использовать Ti.App.myGlobalVar = "ImAGlobalVar" и не нуждаетесь в обратных
чертах
это правильный ответ, по крайней мере для Node.js. делаю expect = require('chai').expect;марку expectпеременной доступны во всех моих тестовых файлах!
pocesar
11

Вы можете передать опцию -b, когда компилируете код с помощью coffee-script в файле node.js. Скомпилированный код будет таким же, как на coffeescript.org.

phongnh
источник
Как? Где мне поставить опцию -b?
Гарри
1
@Harry - -b/ --bareидет сразу после coffeeкоманды.
ocodo
9

Добавить к ответу Иво Ветцеля

Кажется, существует сокращенный синтаксис, exports ? thisкоторый я могу найти только документированным / упомянутым в публикации группы Google .

То есть на веб-странице, чтобы сделать функцию доступной глобально, вы снова объявляете функцию с @префиксом:

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

<a href="javascript:aglobalfunction()" >Click me!</a>
atomicules
источник
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

Это работает, потому что при вызове Referenceк Function«голым» (то есть func(), вместо того , new func()или obj.func()), что - то обычно называют «функцией вызова вызова шаблона», всегда связывается thisс глобальным объектом для этого контекста исполнения .

Приведенный выше CoffeeScript просто компилируется в (function(){ return this })(); поэтому мы используем это поведение для надежного доступа к глобальному объекту.

ELLIOTTCABLE
источник
Это великолепно!
металим
Единственное, что сработало для меня. Ненавижу CoffeeScript.
PCV
Люблю CoffeeScript. Пока что это лучший язык программирования. Жаль, что он был создан и поддерживается как хобби-проект, приводящий к хаосу и глупости в его моделях использования.
Металим
3

Поскольку coffeescript редко используется сам по себе, вы можете использовать globalпеременную, предоставляемую либо node.js, либо browserify (и любыми потомками, такими как coffeeify, сценарии сборки gulp и т. Д.).

В node.js globalесть глобальное пространство имен.

В browserify globalравно window.

Итак, просто:

somefunc = ->
  global.variable = 123
metalim
источник