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 applymetody, 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;
evalrozwią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 newponieważ 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.