Welche Vorteile bietet die ES2015 (ES6) -Klassensyntax?

85

Ich habe viele Fragen zu ES6-Klassen.

Was ist der Vorteil der Verwendung von classSyntax? Ich habe gelesen, dass public / private / static Teil von ES7 sein wird. Ist das ein Grund?

Ist es außerdem classeine andere Art von OOP oder ist es immer noch die prototypische Vererbung von JavaScript? Kann ich es mit ändern .prototype? Oder ist es nur dasselbe Objekt, aber zwei verschiedene Arten, es zu deklarieren?

Gibt es Geschwindigkeitsvorteile? Vielleicht ist es einfacher zu pflegen / zu verstehen, wenn Sie eine große Anwendung wie eine große App haben?

ssbb
quelle
Nur meine eigene Meinung: Die neuere Syntax ist viel einfacher zu verstehen und würde nicht viel Vorerfahrung erfordern (zumal die meisten Leute aus einer OOP-Sprache stammen). Das Prototyp-Objektmodell ist jedoch wissenswert, und Sie werden nie erfahren, dass es mit der neuen Syntax auch schwieriger sein wird, an Prototyp-Code zu arbeiten, wenn Sie ihn nicht bereits kennen. Also würde ich meinen eigenen Code mit der alten Syntax schreiben, wenn ich diese Wahl habe
Zach Smith

Antworten:

118

Die neue classSyntax ist, für jetzt , meist syntaktischen Zucker. (Aber Sie wissen, die gute Art von Zucker.) In ES2015-ES2019 classgibt es nichts, was Sie mit Konstruktorfunktionen und Reflect.construct(einschließlich Unterklassen Errorund Array¹) nicht tun können . ( In ES2021 gibt es wahrscheinlich einige Dinge, die Sie damit tun können class, die Sie sonst nicht tun können: private Felder , private Methoden und statische Felder / private statische Methoden .)

Ist es außerdem classeine andere Art von OOP oder immer noch die prototypische Vererbung von JavaScript?

Es ist dieselbe prototypische Vererbung, die wir immer hatten, nur mit einer saubereren und bequemeren Syntax, wenn Sie Konstruktorfunktionen ( new Foousw.) verwenden möchten. (Besonders im Fall der Ableitung von Arrayoder Error, was Sie in ES5 und früher nicht konnten. Sie können jetzt mit Reflect.construct[ spec , MDN ], aber nicht mit dem alten ES5-Stil.)

Kann ich es mit ändern .prototype?

Ja, Sie können das prototypeObjekt im Konstruktor der Klasse weiterhin ändern, nachdem Sie die Klasse erstellt haben. ZB ist das völlig legal:

class Foo {
    constructor(name) {
        this.name = name;
    }

    test1() {
        console.log("test1: name = " + this.name);
    }
}
Foo.prototype.test2 = function() {
    console.log("test2: name = " + this.name);
};

Gibt es Geschwindigkeitsvorteile?

Durch die Bereitstellung hierfür eine spezifische Idiom, nehme ich an, es ist möglich , dass der Motor in der Lage sein kann , eine bessere Optimierung des Jobs zu tun. Aber sie können schon schrecklich gut optimieren, ich würde keinen signifikanten Unterschied erwarten.

Welche Vorteile bietet die ES2015 (ES6) -Syntax class?

Kurz gesagt: Wenn Sie Konstruktorfunktionen überhaupt nicht verwenden Object.create, classist es für Sie nicht hilfreich , sie zu bevorzugen oder ähnlich zu sein.

Wenn Sie Konstruktorfunktionen verwenden, gibt es einige Vorteile für class:

  • Die Syntax ist einfacher und weniger fehleranfällig.

  • Es ist viel einfacher (und wiederum weniger fehleranfällig), Vererbungshierarchien mit der neuen Syntax einzurichten als mit der alten.

  • classschützt Sie vor dem häufigen Fehler, dass newdie Konstruktorfunktion nicht verwendet werden kann (indem der Konstruktor eine Ausnahme auslöst, wenn er thiskein gültiges Objekt für den Konstruktor ist).

  • Das Aufrufen der Version einer Methode des übergeordneten Prototyps ist mit der neuen Syntax viel einfacher als mit der alten ( super.method()anstelle von ParentConstructor.prototype.method.call(this)oder Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)).

Hier ist ein Syntaxvergleich für eine Hierarchie:

// ***ES2015+**
class Person {
    constructor(first, last) {
        this.first = first;
        this.last = last;
    }

    personMethod() {
        // ...
    }
}

class Employee extends Person {
    constructor(first, last, position) {
        super(first, last);
        this.position = position;
    }

    employeeMethod() {
        // ...
    }
}

class Manager extends Employee {
    constructor(first, last, position, department) {
        super(first, last, position);
        this.department = department;
    }

    personMethod() {
        const result = super.personMethod();
        // ...use `result` for something...
        return result;
    }

    managerMethod() {
        // ...
    }
}

Beispiel:

vs.

// **ES5**
var Person = function(first, last) {
    if (!(this instanceof Person)) {
        throw new Error("Person is a constructor function, use new with it");
    }
    this.first = first;
    this.last = last;
};

Person.prototype.personMethod = function() {
    // ...
};

var Employee = function(first, last, position) {
    if (!(this instanceof Employee)) {
        throw new Error("Employee is a constructor function, use new with it");
    }
    Person.call(this, first, last);
    this.position = position;
};
Employee.prototype = Object.create(Person.prototype);
Employee.prototype.constructor = Employee;
Employee.prototype.employeeMethod = function() {
    // ...
};

var Manager = function(first, last, position, department) {
    if (!(this instanceof Manager)) {
        throw new Error("Manager is a constructor function, use new with it");
    }
    Employee.call(this, first, last, position);
    this.department = department;
};
Manager.prototype = Object.create(Employee.prototype);
Manager.prototype.constructor = Manager;
Manager.prototype.personMethod = function() {
    var result = Employee.prototype.personMethod.call(this);
    // ...use `result` for something...
    return result;
};
Manager.prototype.managerMethod = function() {
    // ...
};

Live-Beispiel:

Wie Sie sehen können, gibt es dort viele wiederholte und ausführliche Dinge, die leicht falsch und langweilig sind, um sie erneut einzugeben (weshalb ich damals ein Skript dafür geschrieben habe ).


¹ "In ES2015-ES2018 classgibt es nichts, was Sie mit Konstruktorfunktionen und Reflect.construct(einschließlich Unterklassen Errorund Array) nicht tun können. "

Beispiel:

TJ Crowder
quelle
9
Ich weiß nicht, ob dies ein fairer Vergleich ist. Sie können ein ähnliches Objekt ohne die gesamte Kruft erreichen. Es verwendet die JS-Methode zur Objektverknüpfung über Prototypketten, anstatt sich der klassischen Vererbung anzunähern. Ich habe einen Kern gemacht , der ein einfaches Beispiel zeigt, das auf Ihrem basiert, um zu zeigen, wie man das macht.
Jason Cust
8
@JasonCust: Ja, es ist ein fairer Vergleich, weil ich den ES5-Weg zeige, um das zu tun, was ES6 macht class. JavaScript ist so flexibel, dass Sie keine Konstruktorfunktionen verwenden müssen, wenn Sie dies nicht möchten. Dies ist das, was Sie tun, aber das unterscheidet sich von class- der springende Punkt classist die Vereinfachung der pseudoklassischen Vererbung in JavaScript.
TJ Crowder
3
@ JasonCust: Ja, das ist ein anderer Weg. Ich würde es nicht "einheimischer" nennen (ich weiß, dass Kyle es tut, aber das ist Kyle). Konstruktorfunktionen sind für JavaScript von grundlegender Bedeutung. Sehen Sie sich alle integrierten Typen an (mit der Ausnahme, dass die Anti-Konstruktor-Fraktion es irgendwie geschafft hat, Symboldurcheinander zu kommen ). Aber das Tolle ist wiederum, dass Sie es nicht müssen, wenn Sie sie nicht verwenden möchten. Ich finde in meiner Arbeit, dass Konstruktorfunktionen an vielen Stellen sinnvoll sind und die Semantik newdie Klarheit verbessert; und Object.createmacht an vielen anderen Orten Sinn, insb. Fassadensituationen. Sie ergänzen sich. :-)
TJ Crowder
4
meh. Ich kaufe die Notwendigkeit dafür nicht. Ich trat von c # zurück, um von oop wegzukommen, und jetzt kriecht oop auf Javascript. Ich mag keine Typen. alles sollte eine var / object / function sein. das ist es. keine fremden Schlüsselwörter mehr. und Vererbung ist ein Betrug. wenn Sie einen guten Kampf zwischen Typen und Nichttypen sehen wollen. Schauen Sie sich Seife gegen Ruhe an. und wir alle wissen, wer das gewonnen hat.
Foreyez
3
@foreyez: Durch die classSyntax wird JavaScript nicht mehr typisiert als zuvor. Wie ich in der Antwort sagte, ist es meistens nur eine (viel) einfachere Syntax für das, was bereits da war. Das war absolut notwendig, die Leute haben ständig die alte Syntax falsch verstanden. Es bedeutet auch nicht, dass Sie die Vererbung mehr als zuvor verwenden müssen. (Und wenn Sie niemals "Klassen" mit der alten Syntax einrichten, müssen Sie dies auch nicht mit der neuen Syntax tun.)
TJ Crowder
21

ES6-Klassen sind syntaktischer Zucker für das prototypische Klassensystem, das wir heute verwenden. Sie machen Ihren Code prägnanter und selbstdokumentierender, was Grund genug ist, sie zu verwenden (meiner Meinung nach).

Verwenden von Babel zum Transpilieren dieser ES6-Klasse:

class Foo {
  constructor(bar) {
    this._bar = bar;
  }

  getBar() {
    return this._bar;
  }
}

wird dir so etwas geben wie:

var Foo = (function () {
  function Foo(bar) {    
    this._bar = bar;
  }

  Foo.prototype.getBar = function () {
    return this._bar;
  }

  return Foo;
})();

Die zweite Version ist nicht viel komplizierter, es ist mehr Code zu pflegen. Wenn Sie sich mit Vererbung befassen, werden diese Muster noch komplizierter.

Da die Klassen nach denselben prototypischen Mustern kompiliert werden, die wir verwendet haben, können Sie dieselben Prototypmanipulationen an ihnen durchführen. Dazu gehören das Hinzufügen von Methoden und dergleichen zur Laufzeit, das Zugreifen auf Methoden Foo.prototype.getBarusw.

ES6 bietet heute einige grundlegende Unterstützung für den Datenschutz, obwohl es darauf basiert, dass die Objekte, auf die Sie nicht zugreifen möchten, nicht exportiert werden. Zum Beispiel können Sie:

const BAR_NAME = 'bar';

export default class Foo {
  static get name() {
    return BAR_NAME;
  }
}

und BAR_NAMEsteht anderen Modulen nicht zur direkten Referenzierung zur Verfügung.

Viele Bibliotheken haben versucht, dies zu unterstützen oder zu lösen, wie Backbone mit ihrem extendsHelfer, der einen nicht validierten Hash methodenähnlicher Funktionen und Eigenschaften verwendet, aber es gibt kein einheitliches System zum Aufdecken prototypischer Vererbung, bei dem nicht mit dem Prototyp herumgespielt wird.

Da JS-Code immer komplizierter und die Codebasis größer wird, haben wir begonnen, viele Muster zu entwickeln, um Dinge wie Vererbung und Module zu handhaben. Das IIFE, mit dem ein privater Bereich für Module erstellt wird, verfügt über viele Klammern und Parens. Das Fehlen eines dieser Skripte kann zu einem gültigen Skript führen, das etwas völlig anderes ausführt (das Überspringen des Semikolons, nachdem ein Modul das nächste Modul als Parameter übergeben kann, was selten gut ist).

tl; dr: Es ist Zucker für das, was wir bereits tun und macht Ihre Absicht im Code klar.

ssube
quelle