Clases

ES6 introdujo el concepto de "clases" en JavaScript, que difiere de las clases en otros lenguajes de programación. Aquí, las clases son funciones especiales que sirven como plantillas para crear objetos que ya contienen datos, propiedades asociadas con esos datos y métodos relacionados con la manipulación de esos datos. Estos objetos, propiedades y métodos se denominan, en conjunto, "miembros" de la clase.

Para definir una clase, usa la palabra clave class. Con las prácticas recomendadas y la convención establecida por las funciones de constructor integradas de JavaScript, comienza cualquier identificador de una clase con mayúscula:

class MyClass {}

El objetivo de las clases es proporcionar formas más accesibles de trabajar con funciones avanzadas de prototipos y funciones de constructor:

class MyClass {}

typeof MyClass;
> "function"

Debido a que las clases se agregaron en parte para que trabajar con funciones avanzadas de JavaScript sea más fácil y atractivo, a veces se las llama "azúcar sintáctica". Sin embargo, las clases hacen más que solo proporcionar una abreviatura útil para trabajar con la herencia prototípica. Se crearon oportunidades para abordar problemas de diseño antiguos en JavaScript sin introducir problemas de retrocompatibilidad. Por ejemplo, todo el código dentro del cuerpo de una clase siempre se evalúa en modo estricto.

Para crear una instancia de una clase, usa el operador new.

class MyClass {}

const myClassInstance = new MyClass();

myClassInstance;
> Object { }

Las funciones definidas dentro del cuerpo de una clase se exponen como métodos de cada instancia de esa clase.

class MyClass {
    classMethod() {
        console.log( "My class method." );
    }
}

const myClassInstance = new MyClass();

myClassInstance.classMethod();
> "My class method."

Un método definido dentro de una clase se convierte en un método en el prototipo de la instancia resultante. Debido a la naturaleza de la cadena de prototipos, puedes llamar a estos métodos directamente en el objeto resultante:

class MyClass {
  classMethod() {
    console.log( "My class method." );
  }
}

const myClassInstance = new MyClass( "A string." );

myClassInstance;
> Object { }
    <prototype>: Object { … }
        classMethod: function classMethod()
        constructor: class MyClass { constructor(myPassedValue) }
        <prototype>: Object { … }

myClassInstance.classMethod();
> "My class method."

Cuando se crea una instancia de una clase, se llama a un método constructor() especial que realiza cualquier "configuración" necesaria para la instancia recién creada y, luego, inicializa las propiedades asociadas a ella. Cualquier argumento que se pase a la clase cuando se crea la instancia estará disponible para el método constructor():

class MyClass {
  constructor( myPassedValue ) {
    console.log( myPassedValue );
  }
}

const myClassInstance = new MyClass( "A string." );
> "A string."

Dentro del cuerpo de una clase, el valor de this hace referencia a la instancia en sí, con cualquier propiedad definida en this expuesta como propiedades de cada instancia de esa clase:

class MyClass {
  constructor( myPassedValue ) {
    this.instanceProperty = myPassedValue;
  }
}

const myClassInstance = new MyClass( "A string." );

myClassInstance;
> Object { instanceProperty: "A string." }

Estas propiedades también están disponibles para todos los métodos dentro del cuerpo de la clase:

class MyClass {
  constructor( myPassedValue ) {
    this.instanceProp = myPassedValue;
  }
  myMethod() {
    console.log( this.instanceProp );
  }
}

const myClassInstance = new MyClass( "A string." );

myClassInstance.myMethod();
> "A string."

Si no defines un constructor() para tu clase, el motor de JavaScript usa un constructor vacío "predeterminado". Cada clase solo puede tener un método especial llamado constructor():

class MyClass {
  constructor() {}
  constructor() {}
}
> Uncaught SyntaxError: A class may only have one constructor

Puedes definir una clase mediante una declaración de clase o una expresión de clase. Los ejemplos anteriores fueron todos declaraciones de clase, que requieren que los nombres se invoquen con new. Las expresiones de clase se pueden asignar o dejar sin nombre para crear una clase “anónima”.

let ClassExpression = class {
    constructor() {}
};

ClassExpression;
> class  {}

Puedes usar expresiones de clase anónimas para las funciones que crean clases "sobre la marcha":

function classMaker() {
  return class {
    constructor() {}
  };
}

let MyVariable = classMaker();

MyVariable;
> class  {}

Volver a declarar una clase mediante una declaración de clase genera un error de sintaxis:


class MyClass {
    constructor( ) {
        console.log( "My class." );
    }
};

class MyClass {
    constructor() {
        console.log( "My new class." );
    }
};
> Uncaught SyntaxError: redeclaration of class MyClass

Sin embargo, las expresiones de clase te permiten redefinir una clase:

let ClassExpression = class MyClass { };

ClassExpression = class MyOtherClass {
    constructor( myString ) {
        this.myProp = myString;
    }
};

new ClassExpression( "String." );
> MyOtherClass {myProp: 'String.'}

No puedes invocar una expresión de clase con nombre por ese nombre de la misma forma que una declaración de clase. Sin embargo, el nombre asignado de una expresión de clase está disponible como una propiedad de la instancia creada, principalmente para facilitar la depuración:

let MyVariable = class MyClass {};

MyClass;
> Uncaught ReferenceError: MyClass is not defined

MyVariable;
> class MyClass {}

MyVariable.name;
> "MyClass"

Cuando inicializas una variable mediante una expresión de clase, las reglas de elevación de esa variable se siguen como se espera. Las declaraciones de clase siguen las mismas reglas de "zona muerta temporal" que las de let y const, y se comportan como si no se hubieran elevado a la parte superior de su alcance actual, lo que significa que invocar una clase antes de la declaración de clase provoca un error:

{
    let myVar = new MyClass( "Property string." );

    class MyClass {
        myProp;

        constructor( myString ) {
            this.myProp = myString;
        }
    };
};
> Uncaught ReferenceError: Cannot access 'MyClass' before initialization

Verifica tus conocimientos

¿Cuál de las siguientes opciones define correctamente una clase?

class MyClass {}
myClass = class {}
new class()

¿Cuántos métodos constructor() puede tener una clase?

Uno
Ninguno
Ilimitada