Las clases es como una especie de combo donde allí vas a meter la creación de un object constructor y sus prototipos, a su vez, la posibilidad de crear prototipos estáticos (son propios de la clases, no se pasan a las instancias) y heredar todas o algunas de sus propiedades a otras clases.
Es como una mejora de la sintaxis del object constructor y los métodos de las instancias de esa clase son los prototypes que se definen allí mismo. Se definen clases con sus funciones pero JavaScript manda esas funciones a prototypes que a la final son como métodos.
No obstante es algo orientado a objetos que seguirá evolucionando y de por sí trae semejanza a las clases de otros lenguajes de programación.
Declarar una clase – la palabra reservada class
Para crear una clase usamos la palabra reservada class, seguida del nombre que le vamos a dar a la clase, que por convenio empieza con mayúscula.
Class declaration
Esta es la más usada por mucho. Estoy usando los mismos nombres de clases que usó el instructor.
class Cliente {
El cuerpo de la clase, aquí va el constructor y las funciones prototypes
}
Class expression
Es menos usada, casi no hay ejemplos con ella, pero es bueno conocerla.
const cliente2 = class {
El cuerpo de la clase, aquí va el constructor y las funciones prototypes
}
Instanciando la clase.
Creamos instancias de la clase de la siguiente manera, tanto si la clase fue creada como class declaration o como class expression.
const jaun = new Cliente();
El constructor
Finalmente el objetivo de la clases es para crear objetos. De allí que esto sea llamado programación orientada a objetos. El constructor es indispensable para definir los datos de los objetos de la clase. Esto es un método que va dentro del cuerpo de la clase.
class Cliente {
constructor(dato1, dato2) {
this.nombre = dato1;
this.saldo = dato2;
}
}
const juan = new Cliente('Juan', 400);
console.log(juan);

En lo anterior se declaró la clase y se definió un object constructor. Luego, se definió una instancia de esa clase con los dos datos que espera obtener el object constructor.
Creando métodos o prototypes
Definimos métodos dentro de la clase de la siguiente manera.
class Cliente {
constructor(dato1, dato2) {
this.nombre = dato1;
this.saldo = dato2;
}
mostrarInformacion() {
return `El cliente ${this.nombre} tiene un saldo de ${this.saldo}`;
}
}
const juan = new Cliente('Juan', 400);
console.log(juan);
console.log(juan.mostrarInformacion);

Se creó una función llamada mostrarInformación que lo que hace es retornar un string con los valores nombre y saldo del objeto. JavaScript incluye esta función como un prototype del objeto creado con esa clase que luego puede ser usado como si fuese un método, solo que exclusivo para los objetos instancias de esa clase.
Métodos estáticos – static
Son métodos que no van a estar asociados a las instancias de la clase, si no más bien a la clase en sí. Por lo que no van a ser llamados como métodos de un objeto si no como método de la clase en si. Por lo que la información que van a arrojar será algo genérica. Para ello se usa la palabra reservada «static».
class Cliente {
constructor(dato1, dato2) {
this.nombre = dato1;
this.saldo = dato2;
}
mostrarInformacion() {
return `El cliente ${this.nombre} tiene un saldo de ${this.saldo}`;
}
static bienvenida() {
return `Bienvenido al cajero`;
}
}
const juan = new Cliente('Juan', 400);
console.log(juan);
console.log( juan.mostrarInformacion() );
console.log( Cliente.bienvenida() );

Se creo la función bienvenida que solo arroja un string de caracteres. Para usarla se usa el nombre de la clase con la sintaxis de punto como si fuese un método.
Heredar una clase – extends
Se pueden pasar toda la configuración, constructor, métodos y métodos estáticos, de una clase a otra. Se usa la palabra reservada extends.
class Empresa extends Cliente {
constructor (nombre, saldo, dato3, dato4) { //En este caso, los datos que hereda deben coincidir en nombres
super( nombre, saldo ); //Esto implica this.nombre=dato1 y this.saldo=dato2
this.telefono = dato3;
this.categoria = dato4;
}
static bienvenida() {
return `Bienvenido al cajero de empresas`;
}
}
const empresa = new Empresa('Código Brigzen', 500, 3415230, 'Wordpress');
console.log(empresa);
console.log(empresa.mostrarInformacion());
console.log(Empresa.bienvenida());
Arriba se ha creado la clase Empresa que a su vez ha heredado la clase Cliente. Luego se ha especificado un constructor con cuatro datos, de los cuales los dos primeros ya han sido declarados en el constructor de la clase padre, para traerlos se usa la palabra super y entre paréntesis nos traemos los datos que queramos del constructor de la clase padre. Luego especificamos los nuevos datos que se trabajaran en esta clase, que como ejemplos son teléfono y categoría.
Si se edita alguna función existente en la clase padre, tal edición funcionará para la nueva clase, la clase padre mantiene su función sin editar. En el ejemplo de arriba se usa la función bienvenida para mostrar un ejemplo de una función que se modificó. Y mostrarInformacion como una función no editada que igual la puede usar la nueva clase.

Elemento privado
En JavaScript yo normalmente puedo acceder a los datos de un objeto por medio de la sintaxis de punto. Creo que por el hecho de que esto da cabida a errores, es decir, yo podría suministrar datos a un objeto y luego un colaborador de los códigos podría borrar por accidente alguno que otro.
O por el motivo que sea, existe una posibilidad de que no puedas hacer nada con esos datos. Eso es si yo antepongo un asterisco al nombre de la llave. En el ejemplo de abajo estoy declarando la llave nombre como privada al anteponerle un asterisco. Pero más abajo trato de visualizar ese valor llamándolo desde el objeto creado, por lo que genera un error.
class Cliente {
#nombre;
constructor( dato1, dato2 ) {
this.#nombre = dato1;
this.saldo = dato2
}
mostrarInformacion() {
return `El cliente ${this.#nombre} tiene un saldo de ${this.saldo}`;
}
}
const juan = new Cliente('Juan', 400);
console.log(juan.#nombre); //Esto va a generar un error

Lo podemos arreglar quitando la última línea y a su vez visualizar ese dato desde un método creado en la clase.
class Cliente {
#nombre;
constructor( dato1, dato2 ) {
this.#nombre = dato1;
this.saldo = dato2
}
mostrarInformacion() {
return `El cliente ${this.#nombre} tiene un saldo de ${this.saldo}`;
}
}
const juan = new Cliente('Juan', 400);
console.log(juan.mostrarInformacion());

Actualmente no le veo tanta utilidad o sentido o una mezcla de ambas. Pero ya se aclarará más.