Ostrzeżenie: To jest długi post.
Uprośćmy to. Chcę uniknąć konieczności dodawania nowego operatora za każdym razem, gdy wywołuję konstruktor w JavaScript. Jest tak, ponieważ zwykle zapominam o tym, a mój kod źle się psuje.
Prostym sposobem na obejście tego jest ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Ale potrzebuję tego, aby zaakceptować zmienną nr. takich argumentów ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Pierwszym natychmiastowym rozwiązaniem wydaje się być metoda „zastosuj” w ten sposób ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Jest to NIEPRAWIDŁOWE - nowy obiekt jest przekazywany do apply
metody, a NIE do naszego konstruktora arguments.callee
.
Teraz wymyśliłem trzy rozwiązania. Moje proste pytanie brzmi: które wydaje się najlepsze. Lub, jeśli masz lepszą metodę, powiedz to.
Po pierwsze - użyj eval()
do dynamicznego tworzenia kodu JavaScript, który wywołuje konstruktor.
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...
}
Po drugie - każdy obiekt ma __proto__
właściwość, która jest „tajnym” łączem do jego obiektu prototypowego. Na szczęście ta właściwość jest zapisywalna.
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;
}
Po trzecie - jest to coś podobnego do drugiego rozwiązania.
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
rozwiązanie wydaje się niezdarne i wiąże się ze wszystkimi problemami „ewolucji zła”.__proto__
rozwiązanie jest niestandardowe, a „Great Browser of mIsERY” go nie honoruje.Trzecie rozwiązanie wydaje się zbyt skomplikowane.
Ale dzięki wszystkim powyższym trzem rozwiązaniom możemy zrobić coś takiego, czego nie możemy inaczej ...
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();
Tak skutecznie powyższe rozwiązania dają nam „prawdziwe” konstruktory, które akceptują zmienną nr. argumentów i nie wymagają new
. Jakie jest twoje zdanie na ten temat?
-- AKTUALIZACJA --
Niektórzy powiedzieli „po prostu wyrzuć błąd”. Moja odpowiedź brzmi: robimy ciężką aplikację z ponad 10 konstruktorami i myślę, że byłoby znacznie bardziej przydatne, gdyby każdy konstruktor mógł „mądrze” poradzić sobie z tym błędem bez wyświetlania komunikatów o błędach na konsoli.
Make()
bez new
ponieważ Marka jest aktywowane i dlatego zakłada, że jest to konstruktor
new
? Ponieważ jeśli to jest to drugie, prawdopodobnie pytasz na niewłaściwej stronie. Jeśli jest to pierwsze, możesz nie odrzucić sugestii dotyczących korzystania z nowych i wykrywania błędów tak szybko ... Jeśli Twoja aplikacja jest naprawdę „ciężka”, ostatnią rzeczą, jakiej chcesz, jest jakiś nadmierny mechanizm konstrukcyjny, który ją spowolni. new
, dla całego tego, co dostaje, jest dość szybkie.