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.
quelle
Make()
ohnenew
da Make aktiviert ist , und somit nimmt es ist ein Konstruktornew
? 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.Antworten:
Erstens
arguments.callee
ist es in ES5 strikt veraltet, so dass wir es nicht verwenden. Die wirkliche Lösung ist ziemlich einfach.Sie verwenden überhaupt nicht
new
.Das ist ein richtiger Schmerz im Arsch, oder?
Versuchen
enhance
Das setzt natürlich ES5 voraus, aber jeder nutzt das ES5-Shim, oder?
Sie könnten auch an alternativen js OO-Mustern interessiert sein
Nebenbei können Sie Option zwei durch ersetzen
Wenn Sie Ihr eigenes ES5-
Object.create
Shim haben möchten , ist das ganz einfachquelle
Object.create
. Was ist mit Pre-ES5? ES5-Shim wirdObject.create
als DUBIOUS aufgelistet.__proto__
, dann sind wir immer noch am selben Punkt. Da es vor ES5 keinen einfacheren Weg gibt, den Prototyp zu mutieren. Trotzdem wirkt Ihre Lösung äußerst elegant und zukunftsorientiert. +1 dafür. (Mein Abstimmungslimit ist erreicht)Object.create
Shim ist so ziemlich meine dritte Lösung, aber weniger kompliziert und sieht besser aus als meine natürlich.Die offensichtliche Antwort wäre, das
new
Schlüsselwort nicht zu vergessen .Sie ändern die Struktur und Bedeutung der Sprache.
Was meiner Meinung nach und im Interesse der zukünftigen Betreuer Ihres Codes eine schreckliche Idee ist.
quelle
new
oder nicht, wäre das für mich wartbarer.;
to end-Anweisungen einschließen . (Automatische Semikoloneinfügung)new
oder nicht semantisch identisch . Es gibt keine subtilen Fälle, in denen diese Invariante gebrochen ist. Das ist der Grund, warum es gut ist und warum Sie es verwenden möchten.Die einfachste Lösung besteht darin
new
, sich einen Fehler zu merken und ihn zu werfen, um zu verdeutlichen, dass Sie ihn vergessen haben.Was auch immer du tust, benutze es nicht
eval
. Ich würde es scheuen, Nicht-Standard-Eigenschaften zu verwenden, zum Beispiel,__proto__
weil sie nicht dem Standard entsprechen und sich möglicherweise in ihrer Funktionalität ändern.quelle
.__proto__
Sie es aufIch habe tatsächlich einen Post darüber geschrieben. http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html
Und Sie können es sogar verallgemeinern, damit Sie es nicht jedem Konstruktor hinzufügen müssen. Sie können das sehen, indem Sie meinen Beitrag besuchen
Haftungsausschluss Ich verwende dies nicht in meinem Code, ich habe es nur für den didaktischen Wert gepostet. Ich fand, dass das Vergessen von a
new
ein leicht zu entdeckender Fehler ist. Wie andere glaube ich nicht, dass wir dies für die meisten Codes tatsächlich brauchen. Es sei denn, Sie schreiben eine Bibliothek zum Erstellen einer JS-Vererbung. In diesem Fall könnten Sie diese von einer einzigen Stelle aus verwenden und würden bereits einen anderen Ansatz als die direkte Vererbung verwenden.quelle
var x = new Ctor();
und dann später x als habethis
und tuevar y = Ctor();
, würde sich dies nicht wie erwartet verhalten.this
". Kannst du vielleicht eine Jsfiddle posten, um das potenzielle Problem zu zeigen?Ctor.call(ctorInstance, 'value')
. Ich sehe kein gültiges Szenario für das, was Sie tun. Um ein Objekt zu konstruieren, verwenden Sie entwedervar x = new Ctor(val)
odervar y=Ctor(val)
. Selbst wenn es ein gültiges Szenario gab, ist meine Behauptung, dass Sie Konstruktoren ohne Verwendung haben könnennew Ctor
, nicht, dass Sie Konstruktoren haben können, die unter Verwendung von funktionieren.Ctor.call
Siehe jsfiddle.net/JHNcR/2Wie wäre es damit?
EDIT: Ich habe vergessen hinzuzufügen:
"Wenn Ihre App wirklich" schwer "ist, ist das Letzte, was Sie wollen, ein überarbeiteter Konstruktionsmechanismus, der sie verlangsamt."
Ich stimme voll und ganz zu - wenn Sie ein "Ding" oben ohne das "neue" Schlüsselwort erstellen, ist es langsamer / schwerer als mit diesem Schlüsselwort. Fehler sind dein Freund, weil sie dir sagen, was los ist. Außerdem teilen sie Ihren Mitentwicklern mit, was sie falsch machen.
quelle