J'ai beaucoup de questions sur les classes ES6.
Quel est l'avantage d'utiliser la class
syntaxe? J'ai lu que public / privé / statique fera partie d'ES7, est-ce une raison?
De plus, est-ce class
un autre type de POO ou est-ce toujours l'héritage prototypique de JavaScript? Puis-je le modifier en utilisant .prototype
? Ou est-ce juste le même objet mais deux manières différentes de le déclarer.
Y a-t-il des avantages de vitesse? Peut-être est-il plus facile de maintenir / comprendre si vous avez une grosse application comme une grosse application?
Réponses:
La nouvelle
class
syntaxe est, pour l' instant , principalement du sucre syntaxique. (Mais, vous savez, le bon type de sucre.) Il n'y a rien dans ES2015-ES2020 quiclass
puisse faire que vous ne puissiez pas faire avec les fonctions de constructeur etReflect.construct
(y compris les sousError
- classes etArray
¹). (Il est probable qu'il y aura des choses à ES2021 que vous pouvez faire avecclass
ce que vous ne pouvez pas faire autrement: champs privés , des méthodes privées et les champs statiques / méthodes de statiques privées .)C'est le même héritage prototypique que nous avons toujours eu, juste avec une syntaxe plus propre et plus pratique si vous aimez utiliser des fonctions de constructeur (
new Foo
, etc.). (En particulier dans le cas de la dérivation deArray
ouError
, ce que vous ne pouviez pas faire dans ES5 et les versions antérieures. Vous pouvez maintenant avecReflect.construct
[ spec , MDN ], mais pas avec l'ancien style ES5.)Oui, vous pouvez toujours modifier l'
prototype
objet sur le constructeur de la classe une fois que vous avez créé la classe. Par exemple, c'est parfaitement légal:class Foo { constructor(name) { this.name = name; } test1() { console.log("test1: name = " + this.name); } } Foo.prototype.test2 = function() { console.log("test2: name = " + this.name); };
En fournissant un idiome spécifique pour cela, je suppose qu'il est possible que le moteur puisse faire un meilleur travail d'optimisation. Mais ils sont déjà extrêmement bons pour l'optimisation, je ne m'attendrais pas à une différence significative.
En bref: si vous n'utilisez pas de fonctions de constructeur en premier lieu, préférer
Object.create
ou similaireclass
ne vous est pas utile.Si vous utilisez des fonctions de constructeur, il y a quelques avantages à
class
:La syntaxe est plus simple et moins sujette aux erreurs.
Il est beaucoup plus facile (et encore moins sujet aux erreurs) de configurer des hiérarchies d'héritage en utilisant la nouvelle syntaxe qu'avec l'ancienne.
class
vous défend de l'erreur courante de ne pas utilisernew
avec la fonction constructeur (en demandant au constructeur de lancer une exception si cethis
n'est pas un objet valide pour le constructeur).L'appel de la version du prototype parent d'une méthode est beaucoup plus simple avec la nouvelle syntaxe que l'ancienne (
super.method()
au lieu deParentConstructor.prototype.method.call(this)
ouObject.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this)
).Voici une comparaison de syntaxe pour une hiérarchie:
// ***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() { // ... } }
Exemple:
Afficher l'extrait de code
// ***ES2015+** class Person { constructor(first, last) { this.first = first; this.last = last; } personMethod() { return `Result from personMethod: this.first = ${this.first}, this.last = ${this.last}`; } } class Employee extends Person { constructor(first, last, position) { super(first, last); this.position = position; } personMethod() { const result = super.personMethod(); return result + `, this.position = ${this.position}`; } employeeMethod() { // ... } } class Manager extends Employee { constructor(first, last, position, department) { super(first, last, position); this.department = department; } personMethod() { const result = super.personMethod(); return result + `, this.department = ${this.department}`; } managerMethod() { // ... } } const m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());
contre.
// **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() { // ... };
Exemple en direct:
Afficher l'extrait de code
// **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() { return "Result from personMethod: this.first = " + this.first + ", this.last = " + this.last; }; 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.personMethod = function() { var result = Person.prototype.personMethod.call(this); return result + ", this.position = " + this.position; }; 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); return result + ", this.department = " + this.department; }; Manager.prototype.managerMethod = function() { // ... }; var m = new Manager("Joe", "Bloggs", "Special Projects Manager", "Covert Ops"); console.log(m.personMethod());
Comme vous pouvez le voir, il y a beaucoup de choses répétées et verbeuses qui sont faciles à se tromper et ennuyeuses à retaper (c'est pourquoi j'ai écrit un script pour le faire , à l'époque).
¹ "Il n'y a rien dans ES2015-ES2018 qui
class
puisse faire que vous ne puissiez pas faire avec les fonctions de constructeur etReflect.construct
(y compris les sousError
- classes etArray
)"Exemple:
Afficher l'extrait de code
// Creating an Error subclass: function MyError(...args) { return Reflect.construct(Error, args, this.constructor); } MyError.prototype = Object.create(Error.prototype); MyError.prototype.constructor = MyError; MyError.prototype.myMethod = function() { console.log(this.message); }; // Example use: function outer() { function inner() { const e = new MyError("foo"); console.log("Callng e.myMethod():"); e.myMethod(); console.log(`e instanceof MyError? ${e instanceof MyError}`); console.log(`e instanceof Error? ${e instanceof Error}`); throw e; } inner(); } outer();
.as-console-wrapper { max-height: 100% !important; }
la source
class
. JavaScript est suffisamment flexible pour que vous n'ayez pas à utiliser des fonctions de constructeur si vous ne le souhaitez pas, ce que vous faites, mais c'est différent declass
- le but principalclass
est de simplifier l'héritage pseudo-classique en JavaScript.Symbol
foirer). Mais le grand avantage, encore une fois, c'est que si vous ne voulez pas les utiliser, vous n'êtes pas obligé de le faire. Je trouve dans mon travail que les fonctions de constructeur ont un sens dans de nombreux endroits et que la sémantiquenew
améliore la clarté; et a duObject.create
sens dans de nombreux autres endroits, en particulier. situations de façade. Ils sont complémentaires. :-)class
syntaxe ne rend pas JavaScript plus typé qu'auparavant. Comme je l'ai dit dans la réponse, il s'agit principalement d'une syntaxe (beaucoup) plus simple pour ce qui était déjà là. C'était absolument nécessaire, les gens se trompaient constamment sur l'ancienne syntaxe. Cela ne signifie pas non plus que vous devez utiliser l'héritage plus qu'auparavant. (Et bien sûr, si vous ne configurez jamais de «classes» avec l'ancienne syntaxe, il n'est pas nécessaire de le faire non plus avec la nouvelle syntaxe.)Les classes ES6 sont du sucre syntaxique pour le système de classes prototypique que nous utilisons aujourd'hui. Ils rendent votre code plus concis et auto-documenté, ce qui est une raison suffisante pour les utiliser (à mon avis).
Utilisation de Babel pour transpiler cette classe ES6:
class Foo { constructor(bar) { this._bar = bar; } getBar() { return this._bar; } }
vous donnera quelque chose comme:
var Foo = (function () { function Foo(bar) { this._bar = bar; } Foo.prototype.getBar = function () { return this._bar; } return Foo; })();
La deuxième version n'est pas beaucoup plus compliquée, c'est plus de code à maintenir. Lorsque vous impliquez l'héritage, ces modèles deviennent encore plus compliqués.
Étant donné que les classes se compilent selon les mêmes modèles prototypiques que nous utilisons, vous pouvez effectuer la même manipulation de prototype sur elles. Cela inclut l'ajout de méthodes et autres au moment de l'exécution, l'accès aux méthodes
Foo.prototype.getBar
, etc.Il existe aujourd'hui une prise en charge de base de la confidentialité dans ES6, bien qu'elle soit basée sur le fait de ne pas exporter les objets que vous ne souhaitez pas accéder. Par exemple, vous pouvez:
const BAR_NAME = 'bar'; export default class Foo { static get name() { return BAR_NAME; } }
et
BAR_NAME
ne sera pas disponible pour les autres modules à référencer directement.De nombreuses bibliothèques ont essayé de prendre en charge ou de résoudre cela, comme Backbone avec son
extends
assistant qui prend un hachage non validé de fonctions et de propriétés de type méthode, mais il n'y a pas de système cohérent pour exposer l'héritage prototypique qui n'implique pas de se débrouiller avec le prototype.Au fur et à mesure que le code JS devient plus compliqué et les bases de code plus grandes, nous avons commencé à faire évoluer de nombreux modèles pour gérer des éléments tels que l'héritage et les modules. L'IIFE utilisé pour créer une portée privée pour les modules a beaucoup d'accolades et de parenthèses; manquer l'un de ceux-ci peut entraîner un script valide qui fait quelque chose d'entièrement différent (sauter le point-virgule après qu'un module peut lui passer le module suivant en tant que paramètre, ce qui est rarement bon).
tl; dr: c'est du sucre pour ce que nous faisons déjà et rend votre intention claire dans le code.
la source