Подтвердить что ты не робот

Когда я должен автоматически создавать объект, даже если "новый" забыт?

Скажем, у меня есть следующий конструктор объекта:

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.

Это хорошая идея? Если да, то когда? Когда (и почему) было бы разумно избегать этой техники?

4b9b3361

Ответ 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 - поэтому я бы рассмотрел определяемые пользователем типы как особенно подходящее место для использования этой техники.

См. также: Является ли JavaScript "новым" ключевое слово считается вредным?

Ответ 4

Сторонники "удобочитаемости" могут описывать это как anti-pattern, но вы знаете свой собственный код лучше, чем кто-либо другой - если он работает вы затем идете на это. Лично я предпочел бы один способ сделать одну вещь - это дает ей ясность и краткость. Если другой разработчик входит в битву и пытается использовать мой объект, не создавая его, я бы предпочел, чтобы он сделал ошибку, чтобы сказать ему, что они глупы.