Як визначити глобальні змінні в 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);

компіляція за допомогою кава-скрипту під node.js обгортає так:

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

Документи кажуть:

Якщо ви хочете створити змінні верхнього рівня для інших сценаріїв, використовуйте їх як властивості у вікні або на об'єкт експорту в CommonJS. Екзистенціальний оператор (описаний нижче) дає вам надійний спосіб зрозуміти, куди їх додати, якщо ви орієнтуєтесь як на CommonJS, так і на браузер: root = export? це

Як визначити глобальні змінні, потім в CoffeeScript. Що означає "прикріпити їх як властивості у вікні"?


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 = експортувати? це

Це, очевидно, сценарій кави, тому давайте розберемося, до чого це насправді складається:

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

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

Отже, якщо exportsіснує, що стосується кореня Node.js (або з погано написаним веб-сайтом ...), вкаже на exportsінше this. Так що ж this?

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

Використання .callфункції прив'язує thisвнутрішню функцію до першого переданого параметра, у випадку, коли браузер thisбуде windowоб'єктом, у випадку Node.js це буде глобальний контекст, який також доступний як globalоб'єкт.

Але оскільки у вас є requireфункція в Node.js, немає необхідності присвоювати щось globalоб'єкту в Node.js, натомість ви призначаєте exportsоб'єкт, який потім повертається requireфункцією.

Кава-скрипт

Після цього пояснення, ось що вам потрібно зробити:

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, хоча якщо ви "це" контекст, це вже об'єкт. Це заплутаний синтаксис.
Кевін

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 для створення функції під цим / глобальним простором імен
Рікардо Вілламіл

2
це було так корисно, тепер я можу створювати глобальні об’єкти та функції в окремому сценарії кави
Дієго Фернандо Мурільо Валенсі

Це набагато краще. Переведення JS в CS потрібно було мені, щоб змінити багато функціональних викликів, щоб використовувати об’єкт вікна, тепер я можу це повернути
casraf

33

Іво прибив це, але я зазначу, що ви можете використовувати один брудний трюк, хоча я не рекомендую його, якщо ви збираєтеся вказувати стильові моменти: Ви можете вставляти код JavaScript безпосередньо у свій CoffeeScript, уникаючи його за допомогою посилань.

Однак ось, як правило, це погана ідея: компілятор CoffeeScript не знає про ці змінні, а це означає, що вони не будуть дотримуватися звичайних правил визначення сценарію CoffeeScript. Тому,

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

компілює до

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

і тепер у вас є два foos в різних сферах. Немає способів змінити глобальний 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 під час компіляції коду за допомогою сценарію кави під node.js. Скомпільований код буде таким самим, як і на coffeescript.org.


Як? Де я поміщаю опцію -b?
Гаррі

1
@Harry - -b/ --bareпереходить безпосередньо після coffeeкоманди.
окудо

9

Щоб додати відповідь Іво Ветцеля

Здається, існує короткий синтаксис, exports ? thisякий я можу знайти лише задокументований / згаданий у груповій публікації Google .

Тобто на веб-сторінці, щоб зробити функцію доступною в усьому світі, ви знову оголошуєте функцію з @префіксом:

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

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

9
'@' У @aglobalfunction просто замінюється на 'this'. Це працює, оскільки сфера функції обгортки 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

Це спрацьовує, тому що, коли викликає Referencea Function"голий" (тобто func()замість new func()або obj.func()), щось, що зазвичай називається "шаблон виклику функції-виклику", завжди пов'язується thisз глобальним об'єктом для цього контексту виконання .

Версія CoffeeScript вище просто компілюється в (function(){ return this })(); тому ми здійснюємо таку поведінку для надійного доступу до глобального об’єкта.


Це геніально!
металлім

Єдине, що працювало на мене. Ненависть CoffeeScript.
pcv

Love CoffeeScript. Поки що це найкраща мова програмування. Шкода, що він був створений та підтримувався як проект хобі, що призводить до хаосу та глупості в його моделях використання.
металлім

3

Оскільки coffeescript рідко використовується самостійно, ви можете використовувати globalзмінну, надану або node.js, або переглядати (і будь-які нащадки, такі як coffeeify, gulp build script тощо).

У node.js global- глобальний простір імен.

У перегляді globalдорівнює рівню window.

Отже, просто:

somefunc = ->
  global.variable = 123
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.