Warnung: Dies ist ein langer Beitrag.
Lassen Sie es uns einfach halten. Ich möchte vermeiden, dass dem neuen Operator jedes Mal ein Präfix vorangestellt werden muss, wenn ich einen Konstruktor in JavaScript aufrufe. Das liegt daran, dass ich es oft vergesse und mein Code schlecht funktioniert.
Der einfache Weg, dies zu umgehen, ist ...
function Make(x) {
if ( !(this instanceof arguments.callee) )
return new arguments.callee(x);
// do your stuff...
}
Aber ich brauche dies, um die Variable Nr. Zu akzeptieren. von Argumenten wie diesem ...
m1 = Make();
m2 = Make(1,2,3);
m3 = Make('apple', 'banana');
Die erste unmittelbare Lösung scheint die "Apply" -Methode wie diese zu sein ...
function Make() {
if ( !(this instanceof arguments.callee) )
return new arguments.callee.apply(null, arguments);
// do your stuff
}
Dies ist jedoch FALSCH - das neue Objekt wird an die apply
Methode und NICHT an unseren Konstruktor übergeben arguments.callee
.
Jetzt habe ich drei Lösungen gefunden. Meine einfache Frage ist: Welches scheint am besten zu sein. Oder wenn Sie eine bessere Methode haben, sagen Sie es.
Erstens : Verwenden Sie eval()
diese Option, um dynamisch JavaScript-Code zu erstellen, der den Konstruktor aufruft.
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...
}
Zweitens : Jedes Objekt hat eine __proto__
Eigenschaft, die eine „geheime“ Verknüpfung zu seinem Prototypobjekt darstellt. Zum Glück ist diese Eigenschaft beschreibbar.
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;
}
Drittens - Dies ähnelt der zweiten Lösung.
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
Die Lösung erscheint ungeschickt und bringt alle Probleme von "Eval Eval" mit sich.__proto__
Die Lösung ist nicht standardisiert und wird vom "Great Browser of MISERY" nicht honoriert.Die dritte Lösung scheint zu kompliziert.
Aber mit all den drei oben genannten Lösungen können wir so etwas tun, was wir sonst nicht können ...
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();
Die obigen Lösungen geben uns also effektiv "echte" Konstruktoren, die die Variable Nr. von Argumenten und erfordern nicht new
. Wie sehen Sie das?
- UPDATE -
Einige haben gesagt "wirf einfach einen Fehler". Meine Antwort lautet: Wir machen eine schwere App mit über 10 Konstruktoren, und ich denke, es wäre weitaus umständlicher, wenn jeder Konstruktor diesen Fehler "klug" handhaben könnte, ohne Fehlermeldungen auf die Konsole zu werfen.
Make()
ohne new
da Make aktiviert ist , und somit nimmt es ist ein Konstruktor
new
? Denn wenn es das letztere ist, fragst du wahrscheinlich auf der falschen Seite. Wenn es das erstere ist, möchten Sie möglicherweise Vorschläge zur Verwendung neuer und zum Erkennen von Fehlern nicht so schnell verwerfen ... Wenn Ihre App wirklich "schwer" ist, ist das Letzte, was Sie wollen, ein überarbeiteter Konstruktionsmechanismus, der sie verlangsamt. new
, bei allem Flackern ist es ziemlich flott.