Предупреждение: это длинный пост.
Давайте будем простыми. Я хочу избежать необходимости добавлять префикс оператора new каждый раз, когда я вызываю конструктор в JavaScript. Это потому, что я склонен забывать об этом, и мой код плохо работает.
Простой способ обойти это ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Но мне нужно это, чтобы принять переменную №. аргументов, как это ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Первым немедленным решением, похоже, является метод apply:
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Однако это НЕПРАВИЛЬНО - новый объект передается apply
методу, а НЕ нашему конструктору arguments.callee
.
Теперь у меня есть три решения. Мой простой вопрос: какой из них кажется лучшим. Или, если у вас есть лучший метод, скажите это.
Первый - использовать eval()
для динамического создания кода JavaScript, который вызывает конструктор.
function Make(/* ... */) {
if ( !(this instanceof arguments.callee) ) {
// collect all the arguments
var arr = [];
for ( var i = 0; arguments[i]; i++ )
arr.push( 'arguments[' + i + ']' );
// create code
var code = 'new arguments.callee(' + arr.join(',') + ');';
// call it
return eval( code );
}
// do your stuff with variable arguments...
}
Второе - у каждого объекта есть __proto__
свойство, которое является «секретной» ссылкой на его объект-прототип. К счастью, это свойство доступно для записи.
function Make(/* ... */) {
var obj = {};
// do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// now do the __proto__ magic
// by 'mutating' obj to make it a different object
obj.__proto__ = arguments.callee.prototype;
// must return obj
return obj;
}
Третье - это что-то похожее на второе решение.
function Make(/* ... */) {
// we'll set '_construct' outside
var obj = new arguments.callee._construct();
// now do your stuff on 'obj' just like you'd do on 'this'
// use the variable arguments here
// you have to return obj
return obj;
}
// now first set the _construct property to an empty function
Make._construct = function() {};
// and then mutate the prototype of _construct
Make._construct.prototype = Make.prototype;
eval
Решение кажется неуклюжим и идет со всеми проблемами «зла Эвал».__proto__
решение нестандартное, и «Великий браузер mIsERY» не соблюдает его.Третье решение кажется слишком сложным.
Но со всеми вышеупомянутыми тремя решениями мы можем сделать что-то подобное, что мы не можем иначе ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
m1 instanceof Make; // true
m2 instanceof Make; // true
m3 instanceof Make; // true
Make.prototype.fire = function() {
// ...
};
m1.fire();
m2.fire();
m3.fire();
Таким образом, вышеприведенные решения дают нам «истинные» конструкторы, которые принимают переменную no. аргументов и не требуютnew
. Что ты думаешь об этом?
-- ОБНОВИТЬ --
Кто-то сказал «просто выбросить ошибку». Мой ответ таков: мы создаем тяжелое приложение с более чем 10 конструкторами, и я думаю, что было бы гораздо более утомительно, если бы каждый конструктор мог «разумно» обработать эту ошибку, не выдавая сообщения об ошибках на консоли.
источник
Make()
без,new
потому что Makenew
? Потому что, если это последнее, вы, вероятно, спрашиваете не на том сайте. Если это первое, вы, возможно, захотите не отклонять предложения относительно использования новых и быстрого обнаружения ошибок ... Если ваше приложение действительно "тяжелое", последнее, что вам нужно, - это какой-то перегруженный механизм конструирования, чтобы замедлить его.new
, несмотря на все неудачи, это довольно быстро.Ответы:
в первую очередь
arguments.callee
, устарела в ES5 строго, поэтому мы не используем его. Реальное решение довольно простое.Вы не используете
new
вообще.Это правильная боль в заднице, верно?
Пытаться
enhance
Теперь, конечно, для этого требуется ES5, но каждый использует ES5-прокладку верно?
Вы также можете быть заинтересованы в альтернативных шаблонах JS OO
В качестве альтернативы вы можете заменить второй вариант
Если вам нужна собственная шайба ES5,
Object.create
то это действительно легкоисточник
Object.create
. Как насчет pre ES5? ES5-Shim перечисляетObject.create
как ДУБОВНЫЙ.__proto__
вещи, тогда мы все еще находимся на том же уровне. Потому что до ES5 нет более простого способа поменять прототип. Но в любом случае, ваше решение кажется наиболее элегантным и перспективным. +1 за это. (мой предел голосования достигнут)Object.create
- в значительной степени мое третье решение, но, конечно, оно менее сложное и выглядит лучше, чем мое.Очевидный ответ будет не забывать
new
ключевое слово.Вы меняете структуру и значение языка.
Что, на мой взгляд, и ради будущих разработчиков вашего кода - ужасная идея.
источник
new
или нет, я бы обнаружил, что она более удобна в обслуживании.;
операторы end. (Автоматическая вставка точки с запятой)new
или отсутствие семантически идентичны . Там нет ни одного тонких случаев , когда этот инвариант сломан. Вот почему это хорошо, и почему вы хотите его использовать.Самое простое решение - просто запомнить
new
и выдать ошибку, чтобы сделать очевидным, что вы забыли.Что бы вы ни делали, не используйте
eval
. Я бы не стал использовать нестандартные свойства, например,__proto__
потому что они нестандартные и их функциональность может измениться.источник
.__proto__
этого дьяволаЯ на самом деле написал пост об этом. http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html
И вы можете даже обобщить это, так что вам не нужно добавлять это в начало каждого конструктора. Вы можете увидеть это, посетив мой пост
Отказ от ответственности Я не использую это в своем коде, я только разместил это для дидактического значения. Я обнаружил, что забыть
new
это легко обнаружить. Как и другие, я не думаю, что нам это нужно для большей части кода. Если вы не пишете библиотеку для создания наследования JS, в этом случае вы можете использовать из одного места, и вы уже будете использовать другой подход, чем прямое наследование.источник
var x = new Ctor();
а потом и x, какthis
и у меняvar y = Ctor();
, это не будет работать так , как ожидалось.this
", вы можете опубликовать jsfiddle, чтобы показать потенциальную проблему?Ctor.call(ctorInstance, 'value')
. Я не вижу действительного сценария того, что вы делаете. Чтобы построить объект, вы используетеvar x = new Ctor(val)
либоvar y=Ctor(val)
. Даже если бы был допустимый сценарий, я утверждаю, что у вас могут быть конструкторы без использованияnew Ctor
, а не то, что у вас могут быть конструкторы, работающие с использованием.Ctor.call
См. Jsfiddle.net/JHNcR/2Как насчет этого?
РЕДАКТИРОВАТЬ: я забыл добавить:
«Если ваше приложение действительно« тяжелое », последнее, что вам нужно, это какой-то перегруженный механизм конструирования, чтобы замедлить его»
Я абсолютно согласен - при создании «вещи» выше без ключевого слова «новый» оно медленнее / тяжелее, чем с ним. Ошибки твоего друга, потому что они говорят тебе, что не так. Более того, они сообщают вашим коллегам-разработчикам, что они делают неправильно.
источник