Шаблон для модулей CoffeeScript
Просматривая исходный код CoffeeScript на Github, я заметил, что большинство, если не все, модулей определены следующим образом:
(function() {
...
}).call(this);
Этот шаблон выглядит так, как будто он оборачивает весь модуль в анонимную функцию и вызывает сам себя.
Каковы плюсы (и минусы) этого подхода? Есть ли другие способы достижения тех же целей?
3 ответа
Ответ Хармена довольно хороший, но позвольте мне немного рассказать о том, где это делается компилятором CoffeeScript и почему.
Когда вы что-то компилируете с coffee -c foo.coffee
, вы всегда получите foo.js
это выглядит так:
(function() {
...
}).call(this);
Это почему? Ну, предположим, вы поставили задание как
x = 'stringy string'
в foo.coffee
, Когда он видит это, компилятор спрашивает: x
уже существует в этой области или внешней области? Если нет, это ставит var x
объявление в верхней части этой области в выводе JavaScript.
Теперь предположим, что вы пишете
x = 42
в bar.coffee
, скомпилировать оба и объединить foo.js
с bar.js
для развертывания. Ты получишь
(function() {
var x;
x = 'stringy string';
...
}).call(this);
(function() {
var x;
x = 42;
...
}).call(this);
Итак x
в foo.coffee
и x
в bar.coffee
полностью изолированы друг от друга. Это важная часть CoffeeScript: переменные никогда не просачиваются из одного файла.coffee в другой, если они явно не экспортированы (будучи прикрепленными к общему глобальному или exports
в Node.js).
Вы можете изменить это, используя -b
("голый") флаг coffee
, но это следует использовать только в очень особых случаях. Если бы вы использовали его в приведенном выше примере, вы получите результат:
var x;
x = 'stringy string';
...
var x;
x = 42;
...
Это может иметь ужасные последствия. Чтобы проверить это самостоятельно, попробуйте добавить setTimeout (-> alert x), 1
в foo.coffee
, И обратите внимание, что вам не нужно объединять два файла JS самостоятельно - если вы используете два отдельных <script>
теги, чтобы включить их на странице, они по-прежнему эффективно работают как один файл.
Изолируя области действия различных модулей, компилятор CoffeeScript избавляет вас от головной боли от беспокойства о том, могут ли разные файлы в вашем проекте использовать одни и те же имена локальных переменных. Это обычная практика в мире JavaScript (см., Например, исходный код jQuery или почти любой плагин jQuery) - CoffeeScript просто позаботится об этом за вас.
Хорошая вещь в этом подходе состоит в том, что он создает частные переменные, поэтому не будет никакого конфликта с именами переменных:
(function() {
var privateVar = 'test';
alert(privateVar); // test
})();
alert(typeof privateVar); // undefined
Добавление .call(this)
делает this
Ключевое слово ссылается на то же значение, что и вне функции. Если он не добавлен, this
Ключевое слово будет автоматически ссылаться на глобальный объект.
Вот небольшой пример, показывающий разницу:
function coffee(){
this.val = 'test';
this.module = (function(){
return this.val;
}).call(this);
}
var instance = new coffee();
alert(instance.module); // test
function coffee(){
this.val = 'test';
this.module = (function(){
return this.val;
})();
}
var instance = new coffee();
alert(typeof instance.module); // undefined
Этот синтаксис похож на это:
(function() {
}());
которая называется непосредственной функцией. функция определяется и выполняется немедленно. Преимущества этого в том, что вы можете поместить весь свой код в этот блок и назначить функцию одной глобальной переменной, тем самым уменьшая загрязнение глобального пространства имен. это обеспечивает хороший ограниченный объем внутри функции.
Это типичный шаблон, который я использую при написании модуля:
var MY_MODULE = (function() {
//local variables
var variable1,
variable2,
_self = {},
etc
// public API
_self = {
someMethod: function () {
}
}
return _self;
}());
не уверен, что за минусы могут быть точно, если кто-то еще знает о чем-либо, я был бы рад узнать о них.