Resumen

En ES6, class (clase) se introduce como plantilla para objetos; se puede definir una clase mediante la palabra clave class.

La esencia de class es function.

Se puede considerar como azúcar sintáctico, haciendo que la escritura del prototipo de objetos sea más clara y más parecida a la sintaxis de la programación orientada a objetos.

Uso básico

Definición de clase

Las expresiones de clase pueden ser anónimas o con nombre.

//Clase anónima let Example = class { constructor(a) { this.a = a; } } //Clase con nombre let Example = class Example { constructor(a) { this.a = a; } }

Declaración de clase

class Example { constructor(a) { this.a = a; } }

Punto importante: no se puede declarar repetidamente.

class Example{} class Example{} // Uncaught SyntaxError: Identifier 'Example' has already been // declared let Example1 = class{} class Example{} // Uncaught SyntaxError: Identifier 'Example' has already been // declared

Puntos a tener en cuenta

Las definiciones de clase no se elevan (hoisting), lo que significa que la clase debe definirse antes de acceder a ella; de lo contrario, se producirá un error.

Los métodos de la clase no necesitan la palabra clave function.

No se pueden agregar punto y coma entre métodos.

new Example(); class Example {}

El cuerpo de la clase

Propiedades

prototype

En ES6, prototype todavía existe; aunque se pueden definir métodos directamente en la clase, en realidad los métodos se definen en prototype. Sobrescribir métodos / Agregar métodos durante la inicialización

Example.prototype={ //methods }

Agregar métodos

Object.assign(Example.prototype,{ //methods })

Propiedades estáticas

Propiedades estáticas: son propiedades de la propia clase, es decir, propiedades definidas directamente dentro de la clase (Class.propname), que no requieren instanciación. En ES6 se estipula que dentro de Class solo hay métodos estáticos, no propiedades estáticas.

class Example { //Nueva propuesta static a = 2; } //Escritura actualmente viable Example.b = 2;

Propiedades públicas

class Example{} Example.prototype.a = 2;

Propiedades de instancia

Propiedades de instancia: propiedades definidas en el objeto de instancia (this).

class Example { a = 2; constructor () { console.log(this.a); } }

Propiedad name

Devuelve el nombre de la clase que sigue a class (si existe).

let Example=class Exam { constructor(a) { this.a = a; } } console.log(Example.name); // Exam let Example=class { constructor(a) { this.a = a; } } console.log(Example.name); // Example

Métodos

Método constructor

El método constructor es el método predeterminado de la clase y se llama al crear un objeto instanciado de la clase.

class Example{ constructor(){ console.log('Soy constructor'); } } new Example(); //Soy constructor

Devolver objeto

class Test { constructor(){ //Por defecto devuelve el objeto de instancia this } } console.log(new Test() instanceof Test); // true class Example { constructor(){ //Especificar objeto de retorno return new Test(); } } console.log(new Example() instanceof Example); // false

Métodos estáticos

class Example{ static sum(a, b) { console.log(a+b); } } Example.sum(1, 2); // 3

Métodos de prototipo

class Example { sum(a, b) { console.log(a + b); } } let exam = new Example(); exam.sum(1, 2); // 3

Métodos de instancia

class Example { constructor() { this.sum = (a, b) => { console.log(a + b); } } }

Instanciación de la clase

new

La instanciación de class debe realizarse mediante la palabra clave new.

class Example {} let exam1 = Example(); // Class constructor Example cannot be invoked without 'new'

Objeto instanciado

Compartir objeto prototipo

class Example { constructor(a, b) { this.a = a; this.b = b; console.log('Example'); } sum() { return this.a + this.b; } } let exam1 = new Example(2, 1); let exam2 = new Example(3, 1); //__proto__ está obsoleto, no se recomienda su uso. // console.log(exam1.__proto__ == exam2.__proto__); console.log(Object.getPrototypeOf(exam1) === Object.getPrototypeOf(exam2));// true Object.getPrototypeOf(exam1).sub = function() { return this.a - this.b; } console.log(exam1.sub()); // 1 console.log(exam2.sub()); // 2

decorator

decorator es una función que se utiliza para modificar el comportamiento de la clase y tiene efecto en el momento de la compilación del código.

Decorador de clase

Un parámetro

El primer parámetro target, apunta a la clase en sí.

function testable(target) { target.isTestable = true; } @testable class Example {} Example.isTestable; // true

Múltiples parámetros: implementación anidada

function testable(isTestable) { return function(target) { target.isTestable=isTestable; } } @testable(true) class Example {} Example.isTestable; // true

Propiedades de instancia

Los dos ejemplos anteriores agregan propiedades estáticas; si se desea agregar propiedades de instancia, basta con operar sobre el prototype de la clase.

Decorador de método

3 parámetros: target (objeto prototipo de la clase), name (nombre de la propiedad decorada), descriptor (objeto descriptivo de la propiedad).

class Example { @writable sum(a, b) { return a + b; } } function writable(target, name, descriptor) { descriptor.writable = false; return descriptor; //Debe devolverse }

Orden de ejecución de los decoradores

Entran de afuera hacia adentro, se ejecutan de adentro hacia afuera.

class Example { @logMethod(1) @logMethod(2) sum(a, b){ return a + b; } } function logMethod(id) { console.log('evaluated logMethod'+id); return (target, name, desctiptor) => console.log('excuted logMethod '+id); } // evaluated logMethod 1 // evaluated logMethod 2 // excuted logMethod 2 // excuted logMethod 1

Encapsulamiento y herencia

getter / setter

Definición

class Example{ constructor(a, b) { this.a = a; //Llamar al método set al instanciar this.b = b; } get a(){ console.log('getter'); return this.a; } set a(a){ console.log('setter'); this.a = a; //Llamada recursiva a sí mismo } } let exam = new Example(1,2); //Genera continuamente setter, lo que finalmente provoca RangeError class Example1{ constructor(a, b) { this.a = a; this.b = b; } get a(){ console.log('getter'); return this._a; } set a(a){ console.log('setter'); this._a = a; } } let exam1 = new Example1(1,2); //Solo produce setter, no llama al método getter console.log(exam1._a); //1, se puede acceder directamente

getter no puede aparecer solo

class Example { constructor(a) { this.a = a; } get a() { return this.a; } } let exam = new Example(1); // Uncaught TypeError: Cannot set property // a of #<Example> which has only a getter

getter y setter deben aparecer al mismo nivel

class Father { constructor(){} get a() { return this._a; } } class Child extends Father { constructor(){ super(); } set a(a) { this._a = a; } } let test = new Child(); test.a = 2; console.log(test.a); // undefined class Father1 { constructor(){} //O colocarlos ambos en la subclase get a() { return this._a; } set a(a) { this._a = a; } } class Child1 extends Father1 { constructor(){ super(); } } let test1 = new Child1(); test1.a = 2; console.log(test1.a); // 2

extends

La herencia de clases se implementa mediante extends.

class Child extends Father { ... }

super

En el método constructor de la subclase debe haber super, y debe aparecer antes de this.

class Father { constructor() {} } class Child extends Father { constructor() {} // or // constructor(a) { // this.a = a; // super(); // } } let test = new Child(); // Uncaught ReferenceError: Must call super // constructor in derived class before accessing 'this' or returning // from derived constructor

La llamada al constructor de la clase padre solo puede aparecer en el constructor de la subclase.

class Father { test(){ return 0; } static test1(){ return 1; } } class Child extends Father { constructor(){ super(); } } class Child1 extends Father { test2() { super(); // Uncaught SyntaxError: 'super' keyword unexpected // here } }

Al llamar métodos de la clase padre, super como objeto, en métodos normales apunta al objeto prototipo de la clase padre; en métodos estáticos, apunta a la clase padre.

class Child2 extends Father { constructor(){ super(); //Llamar a un método normal de la clase padre console.log(super.test()); // 0 } static test3(){ //Llamar a un método estático de la clase padre return super.test1+2; } } Child2.test3(); // 3

Puntos a tener en cuenta

No se puede heredar de objetos normales.

var Father = { // ... } class Child extends Father { // ... } // Uncaught TypeError: Class extends value #<Object> is not a constructor or null //Solución Object.setPrototypeOf(Child.prototype, Father);