Introducción
Las clases son una plantilla para crear objetos. Encapsulan datos con código para operar con ellos. En JS, las clases se basan en prototipos, pero también tienen una sintaxis y semántica propias de cada clase.
Las clases son esencialmente “funciones especiales” y, así como se pueden definir expresiones de función y declaraciones de función, una clase se puede definir de dos maneras: una expresión de clase o una declaración de clase.
// Declaration
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}
// Expression; the class is anonymous but assigned to a variable
const Rectangle = class {
constructor(height, width) {
this.height = height;
this.width = width;
}
};
// Expression; the class has its own name
const Rectangle = class Rectangle2 {
constructor(height, width) {
this.height = height;
this.width = width;
}
};Al igual que las declaraciones de funciones, las declaraciones de clases pueden ser anónimas o tener un nombre distinto al de la variable a la que están asignadas. Sin embargo, a diferencia de las declaraciones de funciones, las declaraciones de clases tienen las mismas restricciones temporales de zona muerta que let o const y se comportan como si no se hubieran eliminado.
Cuerpo de la clase
El cuerpo de una clase es la parte entre llaves {}. Aquí se definen los miembros de la clase, como métodos o constructores.
El cuerpo de una clase se ejecuta en modo estricto, incluso sin la directiva "use strict".
Un elemento de clase se puede caracterizar por tres aspectos:
- Tipo: Receptor, Regulador, Método o Campo
- Ubicación: Estática o de muestra
- Visibilidad: Pública o Privada
Constructor
Un método constructor es un método especial para crear e inicializar un objeto creado con una clase. Solo puede haber un método especial llamado "constructor" en una clase; si la clase contiene más de una instancia de un método constructor, se lanzará un error de sintaxis.
Un constructor puede utilizar la palabra clave super para llamar al constructor de la superclase.
Puede crear propiedades de instancia dentro del constructor:
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
}Alternativamente, si los valores de las propiedades de su instancia no dependen de los argumentos del constructor, puede definirlos como campos de clase.
Bloques de inicialización estáticos
Los bloques estáticos permiten la inicialización flexible de propiedades estáticas, incluida la evaluación de expresiones durante la inicialización, al tiempo que permiten el acceso al ámbito privado.
Se pueden declarar múltiples bloques estáticos y estos bloques pueden intercalarse con declaraciones de campos y métodos estáticos (todos los elementos estáticos se evalúan en el orden de declaración).
Métodos
Los métodos se definen en el prototipo de cada instancia de una clase y son compartidos por todas las instancias. Los métodos pueden ser funciones simples, funciones asíncronas, funciones generadoras o funciones generadoras asíncronas. Para más información, consulte Definiciones de métodos.
class Rectangle {
constructor(height, width) {
this.height = height;
this.width = width;
}
// Getter
get area() {
return this.calcArea();
}
// Method
calcArea() {
return this.height * this.width;
}
*getSides() {
yield this.height;
yield this.width;
yield this.height;
yield this.width;
}
}
const square = new Rectangle(10, 10);
console.log(square.area); // 100
console.log([...square.getSides()]); // [10, 10, 10, 10]Métodos y campos estáticos
La palabra clave static define un método o campo estático para una clase. Las propiedades estáticas (campos y métodos) se definen en la propia clase, no en cada instancia. Los métodos estáticos se suelen usar para crear funciones de utilidad para una aplicación, mientras que los campos estáticos son útiles para el almacenamiento en caché, la configuración fija o cualquier otro dato que no necesite repetirse en varias instancias.
class Point {
constructor(x, y) {
this.x = x;
this.y = y;
}
static displayName = "Point";
static distance(a, b) {
const dx = a.x - b.x;
const dy = a.y - b.y;
return Math.hypot(dx, dy);
}
}
const p1 = new Point(5, 5);
const p2 = new Point(10, 10);
p1.displayName; // undefined
p1.distance; // undefined
p2.displayName; // undefined
p2.distance; // undefined
console.log(Point.displayName); // "Point"
console.log(Point.distance(p1, p2)); // 7.0710678118654755Anuncios de campo
Con la sintaxis de declaración de campo de clase, el ejemplo del constructor se puede escribir de la siguiente manera:
class Rectangle {
height = 0;
width;
constructor(height, width) {
this.height = height;
this.width = width;
}
}Los campos de clase son como propiedades de objeto, no variables, por lo que no usamos palabras clave como const para declararlos. En JavaScript, las propiedades privadas usan una sintaxis de identificador especial, por lo que tampoco se deben usar palabras clave modificadoras como public y private.
Como se vio anteriormente, los campos se pueden declarar con o sin valores predeterminados. Los campos sin valores predeterminados no se definen por defecto. Al declarar los campos por adelantado, las definiciones de clase están mejor documentadas y los campos siempre están presentes, lo que facilita la optimización.
Consulte Campos de clase públicos para obtener más información.
Propiedades privadas
Utilizando campos privados, la definición se puede modificar de la siguiente manera.
class Rectangle {
#height = 0;
#width;
constructor(height, width) {
this.#height = height;
this.#width = width;
}
}Es un error hacer referencia a campos privados desde fuera de la clase. Solo se pueden leer o escribir desde el cuerpo de la clase. Al definir elementos que no son visibles fuera de la clase, se garantiza que los usuarios de la clase no dependan de parámetros internos, que pueden cambiar de una versión a otra.
Los campos privados solo se pueden declarar con antelación en una declaración de campo. No se pueden crear posteriormente mediante asignación, como sí ocurre con las propiedades regulares.
Consulte Propiedad privada para obtener más información.
Herencia
La palabra clave extends se utiliza en declaraciones de clase o expresiones de clase para crear una clase como hija de otro constructor (clase o función).
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Dog extends Animal {
constructor(name) {
super(name); // call the super class constructor and pass in the name parameter
}
speak() {
console.log(`${this.name} barks.`);
}
}
const d = new Dog("Mitzie");
d.speak(); // Mitzie barks.Si la subclase tiene un constructor, primero debe llamar a super() antes de usarlo. La palabra clave super también puede usarse para llamar a métodos relacionados con la superclase.
class Cat {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a noise.`);
}
}
class Lion extends Cat {
speak() {
super.speak();
console.log(`${this.name} roars.`);
}
}
const l = new Lion("Fuzzy");
l.speak();
// Fuzzy makes a noise.
// Fuzzy roars.Orden de evaluación
Cuando se evalúa una declaración de clase o una expresión de clase, sus diversos componentes se evalúan en el siguiente orden:
- La cláusula expandida, si la hay, se evalúa primero. Debe evaluarse como una función constructora válida o como valor nulo; de lo contrario, se lanza un TypeError.
- Se extrae el método constructor y, si no está presente, se reemplaza con la implementación predeterminada. Sin embargo, dado que la definición del constructor es solo una definición de método, este paso no es visible.
- Las claves de propiedad de los elementos de clase se evalúan en el orden en que se declaran. Si se evalúa la clave de propiedad, la expresión evaluada con este valor se establece en el valor que rodea a la clase (no en la clase misma). Todavía no se ha evaluado ningún valor de propiedad.
- Los métodos y descriptores de acceso se instalan en el orden en que se declaran. Los métodos de instancia y descriptores de acceso se instalan en la propiedad prototipo de la clase actual, y los métodos y descriptores de acceso estáticos se instalan en la propia clase. Los métodos de instancia y descriptores de acceso privados se reservan para su instalación posterior directamente en la instancia. Este paso no es visible.
- La clase se inicializa con el prototipo especificado por las extensiones y la implementación especificada por el constructor. En todos los pasos anteriores, si una expresión evaluada intenta acceder al nombre de la clase, se genera un error de referencia (ReferenceError) porque la clase aún no se ha inicializado.
- Los valores de los elementos de clase se evalúan en el orden de declaración:
- Para cada campo de instancia (público o privado), se almacena su expresión de inicialización. La inicialización se evalúa al crear la instancia, al inicio del constructor (para clases base) o inmediatamente antes del retorno de la llamada super() (para clases derivadas).
- Para cada campo constante (público o privado), su inicialización con este conjunto se evalúa en la clase misma y la propiedad se crea en la clase.
- Los bloques de inicialización estáticos se evalúan con este conjunto en la propia clase.
Ejemplos
Conectando esto con métodos de instancia y estáticos
Cuando se llama a un método estático o de instancia sin un valor definido, por ejemplo, al asignar el método a una variable y luego llamarla, el valor no estará definido dentro del método. Este comportamiento es el mismo incluso sin la directiva "using strict", ya que el código dentro del cuerpo de la clase siempre se ejecuta en modo estricto.
class Animal {
speak() {
return this;
}
static eat() {
return this;
}
}
const obj = new Animal();
obj.speak(); // the Animal object
const speak = obj.speak;
speak(); // undefined
Animal.eat(); // class Animal
const eat = Animal.eat;
eat(); // undefinedSi reescribimos lo anterior utilizando la sintaxis tradicional basada en funciones en el caso no estricto, las llamadas a este método se restringen automáticamente a globalThis. En el caso estricto, el valor de this permanece indefinido.
function Animal() {}
Animal.prototype.speak = function () {
return this;
};
Animal.eat = function () {
return this;
};
const obj = new Animal();
const speak = obj.speak;
speak(); // global object (in non–strict mode)
const eat = Animal.eat;
eat(); // global object (in non-strict mode)









