una tipos llamar herencia ecmascript como clases clase javascript ecmascript-6

javascript - tipos - ¿Qué beneficios proporciona la sintaxis `class` de ES2015(ES6)?



herencia de clases javascript (2)

Depende (casi) completamente de usted si usa la nueva sintaxis de class . Principalmente es solo azúcar sintáctico. (Pero, ya sabes, el buen tipo de azúcar.) No hay nada en ES2015-ES2018 que la class pueda hacer que no puedas hacer con las funciones de constructor y Reflect.construct (incluidas las subclases Error y Array ¹). ( Es probable que haya algo en ES2019 o ES2020 que pueda hacer con la class que no puede hacer de otra manera: campos privados y métodos privados ).

Además, ¿es la class un tipo diferente de OOP o sigue siendo la herencia prototípica de JavaScript?

Es la misma herencia prototípica que siempre hemos tenido, solo que con una sintaxis más limpia y conveniente si te gusta usar funciones de constructor ( new Foo , etc.). (Particularmente en el caso de derivar de Array o Error , que no podía hacer en ES5 y versiones anteriores. Ahora puede Reflect.construct con Reflect.construct [ spec , MDN ], pero no con el antiguo estilo ES5).

¿Puedo modificarlo usando .prototype ?

Sí, aún puede modificar el objeto prototype en el constructor de la clase una vez que haya creado la clase. Por ejemplo, esto es perfectamente 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); };

¿Hay beneficios de velocidad?

Al proporcionar un idioma específico para esto, supongo que es posible que el motor pueda hacer un mejor trabajo de optimización. Pero ya son terriblemente buenos para optimizar, no esperaría una diferencia significativa.

¿Qué beneficios ofrece la sintaxis de class ES2015 (ES6)?

Brevemente: si no utiliza funciones de constructor en primer lugar, prefiere Object.create o similar, la class no es útil para usted.

Si usa funciones de constructor, hay algunos beneficios para la class :

  • La sintaxis es más simple y menos propensa a errores.

  • Es mucho más fácil (y de nuevo, menos propenso a errores) configurar jerarquías de herencia utilizando la nueva sintaxis que con la antigua.

  • class defiende del error común de no usar new con la función de constructor (haciendo que el constructor arroje una excepción si this no es un objeto válido para el constructor).

  • Llamar a la versión del prototipo principal de un método es mucho más simple con la nueva sintaxis que el super.method() anterior ( super.method() lugar de ParentConstructor.prototype.method.call(this) u Object.getPrototypeOf(Object.getPrototypeOf(this)).method.call(this) ).

Aquí hay una comparación de sintaxis para una jerarquía:

// ***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() { // ... } }

Ejemplo:

// ***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());

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() { // ... };

Ejemplo en vivo:

// **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());

Como puede ver, hay muchas cosas repetidas y detalladas que son fáciles de equivocar y aburridas de volver a escribir (por eso escribí un guión para hacerlo , en el día).

¹ "No hay nada en ES2015-ES2018 que pueda hacer una class que no pueda hacer con las funciones de constructor y Reflect.construct (incluidas las subclases Error y Array )"

Ejemplo:

// 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; }

Tengo muchas preguntas sobre las clases de ES6.

¿Cuál es el beneficio de usar class sintaxis de class ? Leí que público / privado / estático será parte de ES7, ¿es esa una razón?

Además, ¿es la class un tipo diferente de OOP o sigue siendo la herencia prototípica de JavaScript? ¿Puedo modificarlo usando .prototype ? ¿O es simplemente el mismo objeto pero dos formas diferentes de declararlo?

¿Hay beneficios de velocidad? ¿Tal vez es más fácil de mantener / comprender si tiene una aplicación grande como una gran aplicación?


Las clases ES6 son azúcar sintáctica para el sistema de clases prototípico que usamos hoy. Hacen que su código sea más conciso y autodocumentado, lo cual es razón suficiente para usarlos (en mi opinión).

Usando Babel para transpilar esta clase ES6:

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

te dará algo como:

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

La segunda versión no es mucho más complicada, es más código para mantener. Cuando se involucra la herencia, esos patrones se vuelven aún más complicados.

Debido a que las clases se compilan en los mismos patrones prototípicos que hemos estado usando, puede hacer la misma manipulación de prototipo en ellos. Eso incluye agregar métodos y similares en tiempo de ejecución, acceder a métodos en Foo.prototype.getBar , etc.

Hoy en día, hay un soporte básico para la privacidad en ES6, aunque se basa en no exportar los objetos a los que no desea acceder. Por ejemplo, puedes:

const BAR_NAME = ''bar''; export default class Foo { static get name() { return BAR_NAME; } }

y BAR_NAME no estará disponible para que otros módulos BAR_NAME referencia directamente.

Muchas bibliotecas han intentado apoyar o resolver esto, como Backbone con su ayudante extends que toma un hash no validado de funciones y propiedades similares a los métodos, pero no hay un sistema consistente para exponer la herencia prototípica que no implica perder el tiempo con el prototipo .

A medida que el código JS se vuelve más complicado y las bases de código más grandes, hemos comenzado a desarrollar muchos patrones para manejar cosas como la herencia y los módulos. El IIFE utilizado para crear un ámbito privado para módulos tiene muchos aparatos ortopédicos y parens; perder uno de esos puede dar como resultado un script válido que hace algo completamente diferente (omitir el punto y coma después de que un módulo puede pasarle el siguiente módulo como parámetro, lo que rara vez es bueno).

tl; dr: es azúcar para lo que ya hacemos y deja su intención clara en el código.