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