Когда я должен автоматически создавать объект, даже если "новый" забыт?
Скажем, у меня есть следующий конструктор объекта:
function Foo(bar) {
this.bar = bar;
}
Если я запустил функцию в глобальной области действия без ключевого слова new
, тогда bar
будет установлен в любой области Foo()
, вызываемой в:
var foo = Foo(42);
console.log(bar); // 42
console.log(foo.bar); // ERROR
Итак, моя идея - сделать что-то вроде этого:
function Foo(bar) {
if(!(this instanceof Foo)) {
// return a Foo object
return new Foo(bar);
}
this.bar = bar;
}
Таким образом, если я делаю new Foo(42)
или Foo(42)
, он будет всегда возвращать объект Foo
.
Это хорошая идея? Если да, то когда? Когда (и почему) было бы разумно избегать этой техники?
Ответы
Ответ 1
Это может быть полезно, если вы хотите внутренне построить обертку объекта.
Немного известная библиотека внутренне использует этот подход, jQuery.
Они больше не используют подход instanceof
. Каждый раз, когда вызывается jQuery, он автоматически делает это:
// Define a local copy of jQuery
jQuery = function( selector, context ) {
// Need init if jQuery is called (just allow error to be thrown if not included)
return new jQuery.fn.init( selector, context );
}
Все, что он делает, внутренне ссылается на эту локальную копию. В самом конце своей работы он затем привязывает его к глобальному охвату
window.jQuery = window.$ = jQuery;
Поэтому каждый раз, когда вы вызываете $()
, он внутренне использует new
. Также предполагается, что вы не используете new
извне, но на самом деле все равно, делаете вы или нет.
изменить
jsFiddle Demo
//Foo entrance
function Foo(bar){
//construct a new Foo object to return
return new Foo.prototype.build(bar);
}
//constructor for returning new prototype
Foo.prototype.build = function(bar){
//initialize base settings
this.bar = bar;
//chain object
return this;
};
//this is the complex part
//tie the prototype of Foo.prototype.build.prototype to Foo.prototype
//so that is "seems" like Foo is the parent of the prototype assignments
//while allowing for the use of returning a new object in the Foo entrance
Foo.prototype.build.prototype = Foo.prototype;
//simple expansions at this point, nothing looks too different
//makes a function for a constructed Foo that logs hello
Foo.prototype.hello = function(){
console.log("Hello "+this.bar+" :)");
//returns this for chaining
return this;
};
//more extensions, this one changes this.bar value
Foo.prototype.setBar = function(arg){
//accesses the current Foo instance .bar property
this.bar = arg;
//returns this for chianing
return this;
};
//should be seeing a pattern
Foo.prototype.goodbye = function(){
console.log("Bye "+this.bar+" :(");
return this;
};
var foo = Foo(42);
//console.log(bar); // ERROR
console.log(foo.bar); // 42
foo.hello(); //Hello 42 :)
foo.hello().setBar(9001).goodbye(); //Hello 42 :) Bye 9001 :(
Foo(12).hello(); //Hello 12 :)
Ответ 2
Пока у меня нет ничего против этого стиля, я бы не использовал его просто для того, чтобы быть последовательным. Конечно, я могу сделать все мои конструкторы такими, но писать было бы намного больше.
Если я беспокоюсь о случайном вызове конструктора без new
, я предпочел бы использовать JSHint, чтобы предупредить меня об этом. См. http://www.jshint.com/docs/options/#newcap.
Ответ 3
У меня есть одна хорошая причина, чтобы избежать этого: если вы не забываете использовать new
, тогда это просто лишние накладные расходы. Вы никогда не заметите этого, если не создаете тысячи экземпляров данного объекта, но он все еще существует.
Я рекомендую ограничить количество мест в вашем коде, где выполняется создание объекта на основе new
- если вы не полагаетесь на него в любом месте, где вам нужен новый объект, тогда вам не нужно беспокоиться о том, чтобы забыть это в одном из них. Существуют многочисленные шаблоны, которые могут помочь вам здесь, но самый простой - просто сделать одну функцию где-то ответственной за создание объекта и отложить на это каждый раз - независимо от того, создает 1 экземпляр или 1 000 000 экземпляров зависит от вас.
Тем не менее, если это не имеет смысла, то такой вид охраны - отличный отступ. Обратите внимание, что он имеет много общего с тем, как встроенные конструкторы типов JavaScript дублируются как преобразователи типов при использовании без new
- поэтому я бы рассмотрел определяемые пользователем типы как особенно подходящее место для использования этой техники.
Ответ 4
Сторонники "удобочитаемости" могут описывать это как anti-pattern, но вы знаете свой собственный код лучше, чем кто-либо другой - если он работает вы затем идете на это. Лично я предпочел бы один способ сделать одну вещь - это дает ей ясность и краткость. Если другой разработчик входит в битву и пытается использовать мой объект, не создавая его, я бы предпочел, чтобы он сделал ошибку, чтобы сказать ему, что они глупы.