Попередження: Це довгий пост.
Давайте будемо просто. Я хочу уникати префіксації нового оператора кожного разу, коли я викликаю конструктор у 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');
Першим негайним рішенням здається метод "застосувати", як цей ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Однак це WRONG - новий об'єкт передається 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__
рішення є нестандартним, і "Великий браузер МІСЕРІ" це не шанує.Третє рішення видається надто складним.
Але з усіма перерахованими вище трьома рішеннями ми можемо зробити щось подібне, що не можемо інакше ...
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();
Настільки ефективно наведені вище рішення дають нам "справжні" конструктори, які приймають змінну "ні". аргументів і не вимагають new
. Що ти береш за це?
- ОНОВЛЕННЯ -
Деякі казали "просто кинь помилку". Моя відповідь така: ми робимо важкий додаток з 10+ конструкторами, і я думаю, що було б набагато смішніше, якби кожен конструктор міг «розумно» впоратися з цією помилкою, не кидаючи повідомлення про помилки на консоль.
Make()
без цього, new
тому що Make з великої літери і, таким чином, передбачає, що це конструктор
new
? Тому що якщо це остання, ви, ймовірно, запитуєте на неправильному сайті. Якщо це колишній варіант, ви, можливо, захочете не відхиляти пропозиції щодо використання нових та виявлення помилок настільки швидко ... Якщо ваш додаток справді "важкий", останнє, що ви хочете, - це якийсь переповнений механізм побудови, щоб уповільнити його. new
, зважаючи на все, що він отримує, він досить швидкий.