Deathmatch: Self Executing Anonymous Function -vs- "новая функция"
Ответ встроен ниже в UPDATE 2/ANSWER
Спасибо Джозефу за то, что он помог мне найти ответ (хотя мне это не нравится =).
ОРИГИНАЛЬНЫЙ ВОПРОС
Проводя некоторое исследование лучших практик при использовании именных слов в JavaScript, я натолкнулся на это определение "Model Pattern": http://yuiblog.com/blog/2007/06/12/module-pattern/.
Я обычно использовал этот шаблон, так как видел его в YUI2 года назад, и эта статья дает хороший обзор концепции. Но на что это не влияет, почему "Self Executing Anonymous Functions" используется вместо "новой функции". Это было задано в комментариях, но автор не был хорошо описан. Поскольку этой статье 4+ года (и я не нашел ответ в другом месте в Интернете), я думал, что приведу его здесь.
Это уже в закрытии, так? (см. Почему эта функция заключена в круглые скобки, а затем скобки?, которая также не отвечает на мой вопрос =).
Предполагая следующий установочный код.
var MyNamespace = window.MyNamespace || {};
Какое из них является предпочтительным и почему?
MyNamespace.UsingNew = new function() {
var fnPrivate = function() {
return "secrets1";
};
this.property = "value1";
this.method = function() {
return "property = " + this.property + ' ' + fnPrivate();
}
};
MyNamespace.UsingSelfEx = (function() { //# <- Added "pre-parens" suggested by chuckj
var fnPrivate = function() {
return "secrets2";
};
var fnReturn = {};
fnReturn.property = "value2";
fnReturn.method = function() {
return "property = " + this.property + ' ' + fnPrivate();
}
return fnReturn;
})();
UPDATE:
Кажется, что, подобно jQuery, все классные дети используют "Self Executing Anonymous Functions" (SEAF)! Но я просто этого не понимаю, поскольку я считаю, что использовать более эффективный подход .UsingNew гораздо проще, поскольку вы публикуете публичные функции при определении (а не ниже в возврате, которые необходимо поддерживать отдельно или вынуждать использовать встроенный объект обозначения).
Аргумент о том, что для этого не требуется "this = this", не может содержать воду для меня по ряду причин:
- Чтобы избежать "var that = this", вы получаете либо "var obj", либо оператор return (который должен содержать отдельное определение того, что является общедоступным) или вынужден использовать встроенную запись объекта ( return {1,2,3}) для ваших общедоступных свойств/методов.
- Вы всегда можете сделать приватную переменную "var that = this" в верхней части пространства классов/имен и использовать "это" во всем.
Теперь... Я полагаю, что мой стиль разработки вполне может облегчить управление шаблоном .UsingNew. Мои функции "private" почти всегда "статичны" по своей природе, поэтому мне нужно передать контекст в любом случае (вытесняя необходимость "this"). Я также привык использовать пространство имен аббревиатуры, поэтому, когда мне действительно нужен доступ к "this", я просто ссылаюсь на полный объект через пространство имен аббревиатуры, а не через его полный путь. Например:.
var PrivateFunct = function() {
var rThis = Cn._.val; //# The abbreviated form of Cn.Renderer.Form.Validation
//...
};
или если это Частная статическая функция...
var PrivateStaticFunct = function(oContext) {
//...
};
В других случаях, приведенных выше, я лично считаю, что подход .UsingNew гораздо читабельнее в источнике. У меня довольно обширная база кода, в которой используется шаблон .UsingNew: http://code.google.com/p/cn-namespace/source/browse/Cn.Web/js/Cn/ с Функциональность валидации, вероятно, самая легкая задача, чтобы получить вашу голову в 1 быстром чтении. Я также использую некоторые функции SEAF (см. ErrorMessages.js.aspx), но только тогда, когда они имеют смысл.
Я не мог себе представить, что нужно поддерживать отдельные возвраты, чтобы разоблачить публичные интерфейсы внизу! Тьфу!
Теперь, не поймите меня неправильно, есть несколько мест, где SEAF очень полезен для обеспечения закрытия, но я лично считаю, что это злоупотребление внутри объектов.
ОБНОВЛЕНИЕ 2:
После дальнейшего размышления (и благодаря расширенному обсуждению с Джозефом под его ответом), похоже, существуют некоторые правила, которые могут быть применены к этому DEATHMATCH:
Per Douglas Crockford (см.: JS мы вряд ли начинаем Ya) Анонимные функции никогда не должны использовать "новый", потому что:
- Быстрее использовать литерал объекта.
- Используя new для вызова функции, объект удерживается на бесполезном объекте прототипа. Это отнимает память без компенсирующего преимущества. Если мы не используем новое, мы не храним потраченный впустую прототип объекта в цепочке. (ПРИМЕЧАНИЕ. Прототипные вызовы появляются после определения конструктора, и поскольку SEAF или "новые" анонимные функции запускаются в ближайшее время, нельзя использовать с ними прототип)
- Это требует меньше кода для использования литерала объекта. (хотя это правда, я должен не согласиться с тем, что ненавижу объектную литературную нотацию, я бы предпочел использовать ее, а затем, поскольку она более читаема).
- Не рекомендуется добавлять новые функции непосредственно перед функцией. Например, новая функция не дает преимуществ при построении новых объектов.
Таким образом, кажется, что это мясо и картофель: SEAF предпочтительнее var obj = new function() {...};
из-за скорости и меньших накладных расходов (без ненужного прототипа объекта). То, что вы должны испытывать, - это то, что вы вынуждены использовать объектную нотацию (так, а не между вашими публичными членами) или поддерживать отдельный список общедоступных объектов в возвращаемом объекте.
SEAF не рекомендуется, если вы планируете использовать функцию как конструктор объекта, поскольку instanceof
не будет работать должным образом (см. создание объектов из закрытия JS: следует ли использовать "новое" ключевое слово?).
ОТВЕТ:
- Если это анонимная функция, предназначенная для работы в качестве Singleton/Global Static Instance, используйте SEAF.
-
Если вы намерены использовать его как Constructor
(чтобы потенциально представлять несколько объектов), или вы используете .prototype, используйте "стандартное" определение функции и вызовите "новый", например:
function PseudoClass1() {}
var PseudoClass2 = function() {};
var myClass1 = new PseudoClass1();
var myClass2 = new PseudoClass2();
Я должен сказать, что я не доволен этим ответом;) Я нахожу шаблон .UsingNew MUCH более читаемым в базе кода, но он медленнее и использует больше памяти, чем SEAF, из-за того, что неиспользуемая ссылка на прототип инстанцируется и остается в цепочке объектов.
Ответы
Ответ 1
С одной стороны, этот шаблон:
MyNamespace.UsingNew = new function() {
var fnPrivate = function() {
//what this in here?
return "secrets1";
};
this.property = "value1";
this.method = function() {
//what this in here?
return "property = " + this.property + ' ' + fnPrivate();
}
};
-
использует ключевое слово "новое" для создания экземпляра объекта, который моделируется с использованием функции-конструктора. забыв использовать "новый", вы в конечном итоге называете создание функции MyNamespace.UsingNew()
вместо объекта.
-
это функция-конструктор и еще не экземпляр объекта (пока вы не построите его с помощью нового). вам нужно использовать "this" для обозначения объекта, которым он станет. это просто добавляет проблемы в область видимости, особенно когда вы вставляете в нее больше функций, а значение "this" будет меняться время от времени (и вы не увидите его, пока консоль не сообщит об этом). знакомы с self=this
или that=this
, чтобы сохранить значение "this"? это довольно заметно при использовании этого шаблона
с другой стороны, следующий шаблон несколько лучше (для меня), поскольку:
-
вам не нужно использовать "новое", так как он возвращает объект.
-
не использует "this", поскольку он уже возвращает объект. вам даже не нужно "this".
также, я предпочитаю конструировать другой шаблон таким образом:
ns.myobj = (function(){
//private
var _privateProp = '';
var _privateMeth = function(){};
//public
var publicProp = '';
var publicMeth = function(){};
//expose public
return {
prop:publicProp,
meth:publicMeth
};
}());
Ответ 2
Оба они почти идентичны и должны иметь относительно одни и те же рабочие характеристики. Это просто вопрос стиля. Я, лично, предпочитаю второй, но он написан немного напуганным. Я бы написал это,
MyNamespace.UsingSelfEx = (function () {
function fnPrivate() {
return "secrets2";
}
return {
property: "value2";
method: function () {
return "property = " + this.property + " " + fnPrivate();
}
}
})();
Вам действительно не нужны парады вокруг функции, но функции самоисполнения обычно имеют их и позволяют вначале узнать, что это выполняемая функция.
Ответ 3
Я видел это, и мне это нравилось
var MyThing = (function(){
function MyThing(args){
}
MyThing.prototype = {
prototypeMethod: function() {}
};
return MyThing;
})();
Вы также можете сделать это
MyThing.create = function(args){
return new MyThing(args);
}
Обычно он заключен в другую функцию self invoking.
Во избежание именования столкновений.
(function({
})(window);
И объект window
передается как аргумент,
чтобы присвоить его верхней области.
window.MyThing = MyThing;
В этом примере вы можете использовать статические переменные, частные и т.д.