Wird das "neue" Schlüsselwort von JavaScript als schädlich angesehen? [geschlossen]


568

In einer anderen Frage wies ein Benutzer darauf hin, dass die newVerwendung des Schlüsselworts gefährlich sei, und schlug eine Lösung für die Objekterstellung vor, die nicht verwendet wurde new. Ich habe nicht geglaubt, dass das stimmt, hauptsächlich, weil ich Prototype, Scriptaculous und andere hervorragende JavaScript-Bibliotheken verwendet habe und jeder von ihnen das newSchlüsselwort verwendet hat.

Trotzdem habe ich gestern Douglas Crockfords Vortrag im YUI-Theater gesehen und er sagte genau das Gleiche, dass er das newSchlüsselwort nicht mehr in seinem Code verwendet hat ( Crockford in JavaScript - Akt III: Function the Ultimate - 50:23 Minuten ).

Ist es "schlecht", das newSchlüsselwort zu verwenden? Was sind die Vor- und Nachteile der Verwendung?


90
Es ist NICHT "schlecht", das neue Schlüsselwort zu verwenden. Wenn Sie es jedoch vergessen, rufen Sie den Objektkonstruktor als reguläre Funktion auf. Wenn Ihr Konstruktor seinen Ausführungskontext nicht überprüft, bemerkt er nicht, dass 'this' auf ein anderes Objekt (normalerweise das globale Objekt) anstelle der neuen Instanz verweist. Daher fügt Ihr Konstruktor dem globalen Objekt (Fenster) Eigenschaften und Methoden hinzu. Wenn Sie in der Objektfunktion immer überprüfen, ob 'this' eine Instanz Ihres Objekts ist, tritt dieses Problem nie auf.
Kristian B

5
Ich verstehe das nicht Einerseits rät Doug von der Verwendung ab new. Aber wenn Sie sich die YUI-Bibliothek ansehen. Sie müssen newüberall verwenden. Wie zum Beispiel var myDataSource = new Y.DataSource.IO({source:"./myScript.php"}); .
Aditya_Gaur

2
@aditya_gaur Das liegt daran, dass Sie, wenn Sie eine Initialisierung eines Objekts benötigen, eine initMethode hacken müssen, wenn Sie den Object.createAnsatz verwenden, und diese anschließend aufrufen. Viel einfacher zu verwenden, newwas beides bewirkt, die Prototypenkette festlegt und einen Initialisierungscode aufruft.
Juan Mendes

67
Ich denke nicht, dass dies hätte geschlossen werden sollen. Ja, es wird wahrscheinlich ein Crockford-Anti-Fan-Gift inspirieren, aber wir sprechen über beliebte Ratschläge, um hier ein wichtiges Sprachmerkmal grundsätzlich zu vermeiden. Der Mechanismus, mit dem jedes JQuery-Objekt fast nichts wiegt (was den Speicher betrifft), umfasst die Verwendung des Schlüsselworts 'new'. Ziehen Sie Factory-Methoden dem ständigen Aufrufen neuer Methoden vor, reduzieren Sie jedoch Ihre Architekturoptionen und Ihr Leistungspotenzial nicht drastisch, indem Sie nur Objektliterale verwenden. Sie haben ihren Platz und Konstrukteure haben ihren Platz. Es ist zweifellos veralteter und mieser Rat.
Erik Reppen

2
TLDR: Verwenden newist nicht gefährlich. Auslassen newist gefährlich und daher schlecht . In ES5 können Sie jedoch den Strict Mode verwenden , der Sie vor dieser und vielen anderen Gefahren schützt.
jkdev

Antworten:


603

Crockford hat viel getan, um gute JavaScript-Techniken bekannt zu machen. Seine Stellungnahme zu Schlüsselelementen der Sprache hat viele nützliche Diskussionen ausgelöst. Trotzdem gibt es viel zu viele Menschen, die jede Verkündigung von "schlecht" oder "schädlich" als Evangelium betrachten und sich weigern, über die Meinung eines Mannes hinauszuschauen. Es kann manchmal etwas frustrierend sein.

Die Verwendung der durch das newSchlüsselwort bereitgestellten Funktionalität hat mehrere Vorteile gegenüber der Erstellung jedes Objekts von Grund auf neu:

  1. Vererbung von Prototypen . Die native Vererbungstechnik von JavaScript ist ein einfaches und überraschend effektives Mittel zur Wiederverwendung von Code, obwohl sie von denjenigen, die an klassenbasierte OO-Sprachen gewöhnt sind, oft mit einer Mischung aus Misstrauen und Spott betrachtet wird. Und das neue Schlüsselwort ist das kanonische (und einzige plattformübergreifende) Mittel, um es zu verwenden.
  2. Performance. Dies ist ein Nebeneffekt # 1: Wenn ich für jedes Objekt 10 Methoden hinzufügen möchte ich schaffen, ich könnte nur eine Erstellungsfunktion schreiben , dass manuell jede Methode zu jedem neuen Objekt zuordnet ... Oder ich könnte sie auf die zuweisen Erstellungsfunktionen prototypeund newzum Ausstempeln neuer Objekte. Dies ist nicht nur schneller (es wird kein Code für jede Methode des Prototyps benötigt), sondern es wird auch vermieden, dass jedes Objekt mit separaten Eigenschaften für jede Methode überlagert wird. Auf langsameren Computern (oder insbesondere langsameren JS-Interpreten) kann dies beim Erstellen vieler Objekte zu einer erheblichen Zeit- und Speicherersparnis führen.

Und ja, newhat einen entscheidenden Nachteil, der durch andere Antworten geschickt beschrieben wird: Wenn Sie vergessen, ihn zu verwenden, wird Ihr Code ohne Vorwarnung beschädigt. Glücklicherweise lässt sich dieser Nachteil leicht abmildern - fügen Sie der Funktion selbst einfach ein bisschen Code hinzu:

function foo()
{
   // if user accidentally omits the new keyword, this will 
   // silently correct the problem...
   if ( !(this instanceof foo) )
      return new foo();

   // constructor logic follows...
}

Jetzt können Sie die Vorteile nutzen, newohne sich um Probleme sorgen zu müssen, die durch versehentlichen Missbrauch verursacht wurden. Sie können der Prüfung sogar eine Behauptung hinzufügen, wenn Sie der Gedanke an fehlerhaften Code stört. Oder verwenden Sie, wie einige kommentiert haben, die Prüfung, um eine Laufzeitausnahme einzuführen:

if ( !(this instanceof arguments.callee) ) 
   throw new Error("Constructor called as a function");

(Beachten Sie, dass mit diesem Snippet vermieden werden kann, dass der Name der Konstruktorfunktion fest codiert wird, da das Objekt im Gegensatz zum vorherigen Beispiel nicht tatsächlich instanziiert werden muss. Daher kann es ohne Änderung in jede Zielfunktion kopiert werden.)

John Resig geht in seinem einfachen "Klassen" -Instanzierungsbeitrag ausführlich auf diese Technik ein und fügt standardmäßig ein Mittel hinzu, um dieses Verhalten in Ihre "Klassen" zu integrieren. Auf jeden Fall eine Lektüre wert ... ebenso wie sein bevorstehendes Buch Secrets of the JavaScript Ninja , das in diesem und vielen anderen "schädlichen" Merkmalen der JavaScript-Sprache verborgenes Gold findet (das Kapitel darüber withist besonders aufschlussreich für diejenigen von uns, die es ursprünglich entlassen haben diese vielfach bösartige Funktion als Spielerei).


96
if (! (diese Instanz von Argumenten.callee)) throw Error ("Konstruktor als Funktion aufgerufen"); // Allgemeiner, erfordert keine Kenntnis des Konstruktornamens, lässt den Benutzer den Code korrigieren.
einige

5
Wenn Sie sich Sorgen um die Leistung machen - und tatsächlich Grund dazu haben -, führen Sie die Überprüfung überhaupt nicht durch. Denken Sie entweder daran, es zu verwenden new, oder verwenden Sie eine Wrapper-Funktion, um es für Sie zu speichern. Ich vermute , wenn Sie an dem Punkt angelangt sind, an dem es darauf ankommt, haben Sie bereits andere Optimierungen wie das Auswendiglernen ausgeschöpft, sodass Ihre Erstellungsaufrufe ohnehin lokalisiert werden ...
Shog9

65
Die Verwendung arguments.callee, um zu überprüfen, ob Sie angerufen wurden, ist newmöglicherweise nicht so gut, da arguments.calleesie im strengen Modus nicht verfügbar ist. Verwenden Sie besser den Funktionsnamen.
Sean McMillan

69
Wenn ich eine Kennung falsch schreibe, wird mein Code unterbrochen. Sollte ich keine Bezeichner verwenden? Nicht zu verwenden, newweil Sie vergessen können, es in Ihren Code zu schreiben, ist genauso lächerlich wie mein Beispiel.
Thomas Eding

16
Wenn Sie den strengen Modus aktivieren und vergessen, new zu verwenden, erhalten Sie eine Ausnahme, wenn Sie versuchen, dies zu verwenden: yuiblog.com/blog/2010/12/14/strict-mode-is-coming-to-town Das heißt einfacher als jedem Konstruktor eine Prüfinstanz hinzuzufügen.
Stephenbez

182

Ich habe gerade einige Teile seines Crockfords-Buches "Javascript: The Good Parts" gelesen. Ich habe das Gefühl, dass er alles, was ihn jemals gebissen hat, als schädlich ansieht:

Über Schalter fallen durch:

Ich erlaube niemals, dass Switch-Fälle zum nächsten Fall durchfallen. Ich habe einmal einen Fehler in meinem Code gefunden, der durch einen unbeabsichtigten Durchfall verursacht wurde, unmittelbar nachdem ich eine heftige Rede darüber gehalten hatte, warum Durchfallen manchmal nützlich war. (Seite 97, ISBN 978-0-596-51774-8)

Über ++ und -

Es ist bekannt, dass die Operatoren ++ (Inkrementieren) und - (Dekrementieren) zu schlechtem Code beitragen, indem sie zu übermäßigen Tricks ermutigen. Sie sind nach fehlerhafter Architektur die zweitwichtigsten, wenn es darum geht, Viren und andere Sicherheitsbedrohungen zu aktivieren. (Seite 122)

Über neu:

Wenn Sie beim Aufrufen einer Konstruktorfunktion vergessen, das neue Präfix anzugeben, ist dieses nicht an das neue Objekt gebunden. Leider ist dies an das globale Objekt gebunden. Anstatt Ihr neues Objekt zu erweitern, werden Sie globale Variablen überlisten. Das ist wirklich schlimm Es gibt keine Kompilierungswarnung und keine Laufzeitwarnung. (Seite 49)

Es gibt noch mehr, aber ich hoffe, Sie bekommen das Bild.

Meine Antwort auf Ihre Frage: Nein, es ist nicht schädlich. Aber wenn Sie vergessen, es zu verwenden, wenn Sie sollten, könnten Sie einige Probleme haben. Wenn Sie sich in einer guten Umgebung entwickeln, bemerken Sie das.

Aktualisieren

Ungefähr ein Jahr nach dem Schreiben dieser Antwort wurde die 5. Ausgabe von ECMAScript veröffentlicht, die den strengen Modus unterstützt . Ist im strengen Modus thisnicht mehr an das globale Objekt gebunden, sondern an undefined.


3
Ich stimme vollkommen zu. Die Lösung: Dokumentieren Sie immer, wie Benutzer Ihre Objekte instanziieren müssen. Verwenden Sie ein Beispiel, und Benutzer werden wahrscheinlich ausschneiden / einfügen. In jeder Sprache gibt es Konstrukte / Funktionen, die missbraucht werden können und zu ungewöhnlichem / unerwartetem Verhalten führen. Es macht sie nicht schädlich.
Nicerobot

45
Es gibt eine Konvention, Konstruktoren immer mit einem Großbuchstaben und alle anderen Funktionen mit einem Kleinbuchstaben zu beginnen.
einige

61
Ich habe gerade festgestellt, dass Crockford WHILE nicht für schädlich hält ... Ich weiß nicht, wie oft ich eine Endlosschleife erstellt habe, weil ich vergessen habe, eine Variable
etwa

5
Gleiches gilt für ++, -. Sie drücken genau das, was ich beabsichtige, in einer möglichst klaren Sprache aus. Ich liebe sie! Switch Fall Throughs mögen einigen klar sein, aber ich bin müde von diesen. Ich benutze sie, wenn sie klarer sind (ich kann das Wortspiel nicht widerstehen).
PEZ

30
Meine Antwort an Crockford: Programmieren ist schwer, lass uns einkaufen gehen. Meine Güte!
Jason Jackson

91

Javascript ist eine dynamische Sprache. Es gibt unzählige Möglichkeiten, Fehler zu machen, wo eine andere Sprache Sie aufhalten würde.

Das Vermeiden einer grundlegenden Sprachfunktion, beispielsweise newauf der Grundlage von Unordnung, ist ein bisschen so, als würden Sie Ihre glänzenden neuen Schuhe ausziehen, bevor Sie durch ein Minenfeld gehen, nur für den Fall, dass Ihre Schuhe schlammig werden.

Ich verwende eine Konvention, bei der Funktionsnamen mit einem Kleinbuchstaben beginnen und 'Funktionen', die eigentlich Klassendefinitionen sind, mit einem Großbuchstaben beginnen. Das Ergebnis ist ein wirklich überzeugender visueller Hinweis darauf, dass die 'Syntax' falsch ist:

var o = MyClass();  // this is clearly wrong.

Darüber hinaus helfen gute Namensgewohnheiten. Nachdem alle Funktionen Dinge getan haben, sollte der Name ein Verb enthalten, während Klassen Objekte darstellen und Substantive und Adjektive ohne Verb sind.

var o = chair() // Executing chair is daft.
var o = createChair() // makes sense.

Es ist interessant, wie die Syntaxfärbung von SO den obigen Code interpretiert hat.


8
Ja, ich habe nur das Gleiche über die Syntaxfärbung gedacht.
BobbyShaftoe

4
Einmal habe ich vergessen, das Wort 'Funktion' einzugeben. Das Wort sollte unter allen Umständen vermieden werden. Ein anderes Mal habe ich in einer mehrzeiligen if / then-Anweisung keine geschweiften Klammern verwendet. Jetzt verwende ich keine geschweiften Klammern mehr und schreibe nur einzeilige Bedingungen.
Hal50000

"Das ist eindeutig falsch" . Warum? Für meine Klassen (die es einfach tun if (! this instanceof MyClass) return new MyClass()) bevorzuge ich eigentlich die new-less-Syntax. Warum? Weil Funktionen allgemeiner sind als Konstruktorfunktionen . Das Weglassen newmacht es einfach, eine Konstruktorfunktion in eine reguläre Funktion umzuwandeln ... oder umgekehrt. Durch newdas Hinzufügen wird der Code spezifischer als nötig. Und damit weniger flexibel. Vergleichen Sie mit Java, wo empfohlen wird, Liststattdessen zu akzeptieren, ArrayListdamit der Aufrufer die Implementierung auswählen kann.
Stijn de Witt

41

Ich bin ein Neuling in Javascript, also bin ich vielleicht nicht so erfahren darin, einen guten Standpunkt dazu zu bieten. Dennoch möchte ich meine Meinung zu dieser "neuen" Sache teilen.

Ich komme aus der C # -Welt, in der die Verwendung des Schlüsselworts "neu" so natürlich ist, dass es das Fabrikdesignmuster ist, das mir komisch vorkommt.

Wenn ich zum ersten Mal in Javascript codiere, merke ich nicht, dass es das "neue" Schlüsselwort und den neuen Code wie im YUI-Muster gibt, und es dauert nicht lange, bis ich in eine Katastrophe gerate. Ich verliere den Überblick darüber, was eine bestimmte Zeile tun soll, wenn ich auf den Code zurückblicke, den ich geschrieben habe. Chaotischer ist, dass mein Verstand nicht wirklich zwischen Objektinstanzgrenzen wechseln kann, wenn ich den Code "trocken laufen lasse".

Dann fand ich das "neue" Schlüsselwort, das für mich "Dinge trennt". Mit dem neuen Schlüsselwort werden Dinge erstellt. Ohne das neue Schlüsselwort weiß ich, dass ich es nicht mit dem Erstellen von Dingen verwechseln werde, es sei denn, die von mir aufgerufene Funktion gibt mir starke Hinweise darauf.

Zum Beispiel habe var bar=foo();ich keine Ahnung, welcher Balken möglicherweise sein könnte ... Ist es ein Rückgabewert oder ist es ein neu erstelltes Objekt? Aber var bar = new foo();ich weiß mit Sicherheit, dass Bar ein Objekt ist.


3
Einverstanden, und ich glaube, das Fabrikmuster sollte einer Namenskonvention wie makeFoo ()
pluckyglen

3
+1 - Das Vorhandensein von 'neu' gibt eine klarere Absichtserklärung.
Belugabob

4
Diese Antwort ist etwas seltsam, wenn fast alles in JS ein Objekt ist. Warum müssen Sie sicher sein, dass eine Funktion ein Objekt ist, wenn alle Funktionen Objekte sind?
Joshua Ramirez

@ JoshuaRamirez Der Punkt ist nicht das typeof new foo() == "object". Es newgibt eine Instanz von zurück foo, und Sie wissen, dass Sie foo.bar()und aufrufen können foo.bang(). Dies kann jedoch leicht durch die Verwendung von JsDocs @return gemildert werden. Nicht, dass ich mich für Verfahrenscode einsetze (das Wort meidenew )
Juan Mendes

1
@JuanMendes Hmm ... Ihr Beitrag hat mir den Eindruck vermittelt, dass Ihnen das neue aufgrund des expliziten Schlüsselworts in Ihrer Codebasis gefallen hat. Ich kann das graben. Deshalb verwende ich ein Modulmuster. Ich werde eine Funktion namens createFoo oder NewFoo oder MakeFoo haben, spielt keine Rolle, solange es explizit ist. Darin deklariere ich Variablen, die als Abschlüsse in einem Objektliteral verwendet werden, das von der Funktion zurückgegeben wird. Dieses Objektliteral ist letztendlich Ihr Objekt, und die Funktion war nur eine Konstruktionsfunktion.
Joshua Ramirez

39

Ein weiterer Fall für Neues ist das, was ich Pooh Coding nenne . Winnie the Pooh folgt seinem Bauch. Ich sage, geh mit der Sprache, die du benutzt, nicht dagegen .

Es besteht die Möglichkeit, dass die Betreuer der Sprache die Sprache für die Redewendungen optimieren, die sie zu fördern versuchen. Wenn sie ein neues Schlüsselwort in die Sprache einfügen, halten sie es wahrscheinlich für sinnvoll, beim Erstellen einer neuen Instanz klar zu sein.

Code, der gemäß den Absichten der Sprache geschrieben wurde, wird mit jeder Version effizienter. Und Code, der die Schlüsselkonstrukte der Sprache vermeidet, wird mit der Zeit leiden.

EDIT: Und das geht weit über die Leistung hinaus. Ich kann die Zeiten zählt , die ich gehört habe (oder die) „Warum zum Teufel hat sie das ?“ wenn Sie seltsam aussehenden Code finden. Es stellt sich oft heraus, dass es zu der Zeit, als der Code geschrieben wurde, einen "guten" Grund dafür gab. Das Befolgen des Tao der Sprache ist Ihre beste Versicherung dafür, dass Ihr Code in einigen Jahren nicht verspottet wird.


3
+1 für den Pooh Coding Link - jetzt muss ich Ausreden finden, um dies in meine Gespräche
einzubeziehen

LOL! Ich habe jahrelange Erfahrung in diesem speziellen Bereich und kann Ihnen versichern, dass Sie keine Schwierigkeiten finden werden. Sie nennen mich oft Pooh bei robowiki.net. =)
PEZ

1
Ich weiß nicht, ob Sie diesen Kommentar jemals sehen werden, aber dieser Pooh Coding-Link ist tot.
Calvin

Danke für die Warnung. Wir haben robowiki.net letzte Woche auf ein neues Wiki migriert und der alte Inhalt ist derzeit nicht erreichbar. Ich werde mich beeilen, damit diese alten Links funktionieren.
PEZ

24

Ich habe einen Beitrag darüber geschrieben, wie das Problem des Aufrufs eines Konstruktors ohne das neue Schlüsselwort gemindert werden kann.
Es ist größtenteils didaktisch, zeigt jedoch, wie Sie Konstruktoren erstellen können, die mit oder ohne newfunktionieren, und erfordert nicht, dass Sie in jedem Konstruktor Boilerplate-Code zum Testen hinzufügen this.

http://js-bits.blogspot.com/2010/08/constructors-without-using-new.html

Hier ist der Kern der Technik:

/**
 * Wraps the passed in constructor so it works with
 * or without the new keyword
 * @param {Function} realCtor The constructor function.
 *    Note that this is going to be wrapped
 *    and should not be used directly 
 */
function ctor(realCtor){
  // This is going to be the actual constructor
  return function wrapperCtor(){
    var obj; // object that will be created
    if (this instanceof wrapperCtor) {
      // Called with new
      obj = this;
    } else {
      // Called without new. Create an empty object of the
      // correct type without running that constructor
      surrogateCtor.prototype = wrapperCtor.prototype;
      obj = new surrogateCtor();
    }
    // Call the real constructor function
    realCtor.apply(obj, arguments);
    return obj;
  }

  function surrogateCtor() {}
}

So verwenden Sie es:

// Create our point constructor
Point = ctor(function(x,y){
  this.x = x;
  this.y = y;
});

// This is good
var pt = new Point(20,30);
// This is OK also
var pt2 = Point(20,30);

6
Vermeiden arguments.calleeist großartig!
Gregg Lind

2
surrogateConstructorsollte sein surrogateCtor(oder umgekehrt).
David Conrad

Ich habe eine Sammlung ähnlicher, von Crockford inspirierter Dienstprogramme gepflegt und festgestellt, dass ich immer gestartet bin, um die Implementierungen zu finden, wenn ich ein neues Projekt starte. Und dies schließt einen so grundlegenden Teil der Programmierung ein: die Objektinstanziierung. All diese Arbeit, nur um zufälligen Instanziierungscode zu aktivieren? Ich schätze den Einfallsreichtum dieses Wrappers ehrlich gesagt, aber es scheint nur zu unachtsamer Codierung zu führen.
Joe Coder

1
@joecoder Ich habe erwähnt, dass dies nur für didaktische Zwecke war. Ich abonniere diesen Paranoia-Codierungsstil nicht. Wenn ich jedoch eine Klassenbibliothek schreiben würde, würde ich diese Funktion auf eine Weise hinzufügen, die für Anrufer transparent ist
Juan Mendes

22

Die Gründe für die Nichtverwendung des neuen Schlüsselworts sind einfach:

Wenn Sie es überhaupt nicht verwenden, vermeiden Sie die Gefahr, dass Sie es versehentlich weglassen. Das von YUI verwendete Konstruktionsmuster ist ein Beispiel dafür, wie Sie das neue Schlüsselwort insgesamt vermeiden können. "

var foo = function () {
    var pub= { };
    return pub;
}
var bar = foo();

Alternativ könnten Sie so:

function foo() { }
var bar = new foo();

Auf diese Weise laufen Sie jedoch Gefahr, dass jemand vergisst, das neue Schlüsselwort zu verwenden, und dass dieser Operator nur ein Fubar ist. AFAIK es gibt keinen Vorteil, dies zu tun (außer Sie sind daran gewöhnt).

Am Ende des Tages: Es geht darum, defensiv zu sein. Können Sie die neue Anweisung verwenden? Ja. Macht es Ihren Code gefährlicher? Ja.

Wenn Sie jemals C ++ geschrieben haben, entspricht dies dem Setzen von Zeigern auf NULL, nachdem Sie sie gelöscht haben.


6
Nein. Mit "new foo ()" werden einige Eigenschaften für das zurückgegebene Objekt festgelegt, z. B. der Konstruktor.
einige

34
Um dies klar zu machen: Sie sollten "neu" nicht verwenden, weil Sie es vielleicht vergessen? Du machst Witze, oder?
Bombe

16
@Bombe: In anderen Sprachen würde das Vergessen von "neu" zu einem Fehler führen. In Javascript wird einfach weiter gefahren. Sie können vergessen und nie realisieren. Und wenn man sich nur fehlerhaften Code ansieht, ist das überhaupt nicht offensichtlich, was schief geht.
Kent Fredric

3
@ Greg: Ich verstehe nicht, wie die erste Technik die Verwendung von Prototypenketten ermöglicht - Objektliterale sind großartig, aber die Leistung und andere Vorteile von Prototypen aus Angst wegzuwerfen, scheint ein bisschen albern.
Shog9

5
@Bombe - Sie sollten "neu" verwenden, weil Sie (und jeder, der Ihren Code verwendet) niemals einen Fehler machen werden? Du machst Witze, oder?
Greg Dean

20

Ich denke, "neu" fügt dem Code Klarheit hinzu. Und Klarheit ist alles wert. Gut zu wissen, dass es Fallstricke gibt, aber sie durch Vermeidung von Klarheit zu vermeiden, scheint mir nicht der richtige Weg zu sein.


16

Fall 1: newist nicht erforderlich und sollte vermieden werden

var str = new String('asd');  // type: object
var str = String('asd');      // type: string

var num = new Number(12);     // type: object
var num = Number(12);         // type: number

Fall 2: newist erforderlich, andernfalls wird eine Fehlermeldung angezeigt

new Date().getFullYear();     // correct, returns the current year, i.e. 2010
Date().getFullYear();         // invalid, returns an error

5
Es ist zu beachten, dass in Fall 1 das Aufrufen des Konstruktors als Funktion nur dann sinnvoll ist, wenn Sie eine Typkonvertierung beabsichtigen (und nicht wissen, ob das Hull-Objekt über eine Konvertierungsmethode verfügt, z toString(). Verwenden Sie in allen anderen Fällen Literale. Nicht String('asd') , aber einfach 'asd'und nicht Number(12) , sondern einfach 12.
PointedEars

1
@PointedEars Eine Ausnahme von dieser Regel wäre Array: Array(5).fill(0) ist offensichtlich besser lesbar als [undefined, undefined, undefined, undefined, undefined].fill(0)und(var arr = []).length = 5; arr.fill(0);
yyny

@YoYoYonnY Meine Aussage wurde im Kontext von Fall 1 der Antwort gemacht, der sich auf die Verwendung von newmit Konstruktoren für Objekttypen bezieht, die primitiven Typen entsprechen. Das von Ihnen vorgeschlagene Literal entspricht nicht dem ArrayAufruf (try 0 in …), und der Rest ist ein Syntaxfehler :) Ansonsten sind Sie korrekt, obwohl auch angemerkt werden sollte, dass Array(5)dies mehrdeutig sein kann, wenn Sie nicht konforme Implementierungen in Betracht ziehen (und daher eine emulierte Array.prototype.fill(…) ).
PointedEars

12

Hier ist die kürzeste Zusammenfassung der beiden stärksten Argumente für und gegen die Verwendung des newOperators:

Gegenargument new

  1. Funktionen, die mithilfe des newOperators als Objekte instanziiert werden sollen , können katastrophale Auswirkungen haben, wenn sie fälschlicherweise als normale Funktionen aufgerufen werden. In einem solchen Fall wird der Code einer Funktion in dem Bereich ausgeführt, in dem die Funktion aufgerufen wird, anstatt wie beabsichtigt im Bereich eines lokalen Objekts. Dies kann dazu führen, dass globale Variablen und Eigenschaften mit katastrophalen Folgen überschrieben werden.
  2. Schließlich erscheint es einigen Programmierern hässlich , zu schreiben function Func(), dann aufzurufen Func.prototype und Dinge hinzuzufügen, damit Sie aufrufen können new Func(), um Ihr Objekt zu konstruieren, die aus architektonischen und stilistischen Gründen lieber einen anderen Stil der Objektvererbung verwenden würden.

Weitere Informationen zu diesem Argument finden Sie in Douglas Crockfords großartigem und prägnantem Buch Javascript: The Good Parts. In der Tat überprüfen Sie es trotzdem.

Argument für new

  1. Die Verwendung des newOperators zusammen mit der prototypischen Zuweisung ist schnell.
  2. Das Problem, dass der Code einer Konstruktorfunktion versehentlich im globalen Namespace ausgeführt wird, kann leicht verhindert werden, wenn Sie immer ein bisschen Code in Ihre Konstruktorfunktionen aufnehmen, um zu überprüfen, ob sie korrekt aufgerufen werden, und in den Fällen, in denen dies nicht der Fall ist und den Anruf wie gewünscht entsprechend behandeln.

In John Resigs Beitrag finden Sie eine einfache Erklärung dieser Technik und eine allgemein tiefere Erklärung des von ihm befürworteten Vererbungsmodells.


Ein Update zu den Gegenargumenten: # 1 kann durch Verwendung des 'use strict';Modus (oder der Methode in Ihrem Link) gemildert werden. # 2 Hat in ES6 synatischen Zucker , das Verhalten ist jedoch das gleiche wie zuvor.
NinMonkey

9

Ich stimme Pez und einigen hier zu.

Mir scheint klar, dass "neu" eine selbstbeschreibende Objekterstellung ist, bei der das von Greg Dean beschriebene YUI-Muster vollständig verdeckt ist .

Die Möglichkeit, dass jemand schreiben könnte var bar = foo;oder var bar = baz();wo baz keine Methode zum Erstellen von Objekten ist, scheint weitaus gefährlicher zu sein.


8

Ich denke, neu ist böse, nicht weil es Probleme verursachen kann, wenn Sie vergessen, es versehentlich zu verwenden, sondern weil es die Vererbungskette durcheinander bringt und das Verständnis der Sprache erschwert.

JavaScript ist prototypbasiert objektorientiert. Daher MUSS jedes Objekt so aus einem anderen Objekt erstellt werden var newObj=Object.create(oldObj). Hier wird oldObj als Prototyp von newObj bezeichnet (daher "prototypbasiert"). Dies bedeutet, dass eine Eigenschaft, die in newObj nicht gefunden wird, in oldObj durchsucht wird . newObj ist daher standardmäßig ein leeres Objekt, scheint jedoch aufgrund seiner Prototypkette alle Werte von oldObj zu haben .

Auf der anderen Seite , wenn Sie tun , var newObj=new oldObj()der Prototyp, NEWOBJ ist oldObj.prototype , die unnötig schwer zu verstehen ist.

Der Trick ist zu verwenden

Object.create=function(proto){
  var F = function(){};
  F.prototype = proto;
  var instance = new F();
  return instance;
};

Es ist innerhalb dieser Funktion und nur hier sollte neu verwendet werden. Verwenden Sie danach einfach die Object.create () -Methode. Die Methode löst das Prototypproblem.


7
Um ehrlich zu sein, ich bin nicht wild auf diese Technik - sie fügt nichts Neues hinzu, und wie Ihre Antwort zeigt, kann sie sogar eine Krücke sein. Meiner Meinung nach ist das Verständnis von Prototypketten und Objektinstanziierung entscheidend für das Verständnis von JavaScript ... Wenn es Ihnen jedoch unangenehm ist, sie direkt zu verwenden, ist die Verwendung einer Hilfsfunktion zur Pflege einiger Details in Ordnung, solange Sie sich daran erinnern, was Sie sind tun. FWIW: Eine (etwas nützlichere) Variante ist Teil des ECMAScript 5th ed. std und bereits in einigen Browsern verfügbar - Sie sollten also darauf achten, es nicht blind neu zu definieren!
Shog9

Übrigens: Ich bin mir nicht sicher, warum Sie dieses CW erstellt haben, aber wenn Sie es mit den von mir vorgenommenen Formatierungskorrekturen erneut veröffentlichen möchten, vermeiden Sie dieses Kontrollkästchen ...
Shog9

2
Unnötig schwer zu verstehen? var c = new Car()ist das gleiche wie zu tunvar c = Object.create(Car.prototype); Car.call(c)
Juan Mendes
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.