| [ Web Proxy ] |
| Viewing: https://developer.mozilla.org/es/docs/Web/JavaScript/Guide/Using_classes | [Back] [Original] |
Get to know MDN better
Esta pgina ha sido traducida del ingls por la comunidad. Aprende ms y nete a la comunidad de MDN Web Docs.
JavaScript es un lenguaje basado en prototipos: el comportamiento de un objeto lo determinan sus propias propiedades y las de su prototipo. Sin embargo, con la incorporacin de las clases, la creacin de jerarquas de objetos y la herencia de propiedades y de sus valores se parecen mucho ms a las de otros lenguajes orientados a objetos, como Java. En esta seccin mostraremos cmo se pueden crear objetos a partir de clases.
En muchos otros lenguajes, las clases (o constructores) se distinguen claramente de los objetos (o instancias). En JavaScript, las clases son principalmente una abstraccin sobre el mecanismo de herencia basado en prototipos que ya exista: todos los patrones pueden convertirse a herencia basada en prototipos. Las propias clases tambin son valores normales de JavaScript y tienen sus propias cadenas de prototipos. De hecho, la mayora de las funciones normales de JavaScript pueden usarse como constructores: se usa el operador new con una funcin constructora para crear un objeto nuevo.
En este tutorial trabajaremos con el modelo de clases, bien abstrado, y veremos qu semntica ofrecen las clases. Si quieres profundizar en el sistema de prototipos subyacente, puedes leer la gua Herencia y la cadena de prototipos.
Este captulo da por hecho que ya conoces un poco JavaScript y que has usado objetos normales.
Si tienes algo de experiencia prctica con JavaScript o has seguido la gua, probablemente ya hayas usado clases, aunque no hayas creado ninguna. Por ejemplo, puede que esto te resulte familiar:
const bigDay = new Date(2019, 6, 19);
console.log(bigDay.toLocaleDateString());
if (bigDay.getTime() < Date.now()) {
console.log("rase una vez...");
}
En la primera lnea, creamos una instancia de la clase Date y la llamamos bigDay. En la segunda lnea, llamamos a un mtodo, toLocaleDateString(), de la instancia bigDay, que devuelve una cadena. Despus, comparamos dos nmeros: uno devuelto por el mtodo getTime() y otro llamado directamente desde la propia clase Date, como Date.now().
Date es una clase integrada de JavaScript. A partir de este ejemplo, podemos hacernos una idea bsica de lo que hacen las clases:
new.Estas corresponden a las tres caractersticas clave de las clases:
Las clases se crean normalmente con declaraciones de clase.
class MyClass {
// cuerpo de la clase...
}
Dentro del cuerpo de una clase hay varias caractersticas disponibles.
class MyClass {
// Constructor
constructor() {
// Cuerpo del constructor
}
// Campo de instancia
myField = "foo";
// Mtodo de instancia
myMethod() {
// Cuerpo de myMethod
}
// Campo esttico
static myStaticField = "bar";
// Mtodo esttico
static myStaticMethod() {
// Cuerpo de myStaticMethod
}
// Bloque esttico
static {
// Cdigo de inicializacin esttica
}
// Los campos, los mtodos, los campos estticos y los mtodos estticos
// tienen formas "privadas"
#myPrivateField = "bar";
}
Si vienes de la poca anterior a ES6, quiz te resulte ms familiar usar funciones como constructores. El patrn anterior se traducira, ms o menos, a lo siguiente con funciones constructoras:
function MyClass() {
this.myField = "foo";
// Cuerpo del constructor
}
MyClass.myStaticField = "bar";
MyClass.myStaticMethod = function () {
// Cuerpo de myStaticMethod
};
MyClass.prototype.myMethod = function () {
// Cuerpo de myMethod
};
(function () {
// Cdigo de inicializacin esttica
})();
Nota: Los campos y mtodos privados son caractersticas nuevas de las clases que no tienen un equivalente sencillo en las funciones constructoras.
Una vez declarada una clase, puedes crear instancias de ella con el operador new.
const myInstance = new MyClass();
console.log(myInstance.myField); // 'foo'
myInstance.myMethod();
Las funciones constructoras normales se pueden construir con new y tambin llamar sin new. Sin embargo, intentar "llamar" a una clase sin new produce un error.
const myInstance = MyClass(); // TypeError: Class constructor MyClass cannot be invoked without 'new'
A diferencia de las declaraciones de funciones, las declaraciones de clase no se elevan (o, segn algunas interpretaciones, se elevan pero con la restriccin de la zona muerta temporal), lo que significa que no puedes usar una clase antes de declararla.
new MyClass(); // ReferenceError: Cannot access 'MyClass' before initialization
class MyClass {}
Este comportamiento es parecido al de las variables declaradas con let y const.
Al igual que las funciones, las declaraciones de clase tambin tienen su equivalente como expresin.
const MyClass = class {
// Cuerpo de la clase...
};
Las expresiones de clase tambin pueden tener nombre. El nombre de la expresin solo es visible dentro del cuerpo de la clase.
const MyClass = class MyClassLongerName {
// Cuerpo de la clase. Aqu MyClass y MyClassLongerName apuntan a la misma clase.
};
new MyClassLongerName(); // ReferenceError: MyClassLongerName is not defined
Quiz la tarea ms importante de una clase sea actuar como "fbrica" de objetos. Por ejemplo, cuando usamos el constructor Date, esperamos que nos d un objeto nuevo que represente los datos de fecha que le pasamos, y que despus podamos manipular con otros mtodos que expone la instancia. En las clases, la creacin de instancias la hace el constructor.
Como ejemplo, crearemos una clase llamada Color que representa un color concreto. Los usuarios crean colores pasando un tro de valores RGB.
class Color {
constructor(r, g, b) {
// Asignar los valores RGB como propiedad de `this`.
this.values = [r, g, b];
}
}
Abre las herramientas de desarrollo de tu navegador, pega el cdigo anterior en la consola y crea una instancia:
const red = new Color(255, 0, 0);
console.log(red);
Deberas ver una salida como esta:
Object { values: (3) [] }
values: Array(3) [ 255, 0, 0 ]
Has creado correctamente una instancia de Color, y la instancia tiene una propiedad values, que es un array con los valores RGB que pasaste. Esto equivale prcticamente a lo siguiente:
function createColor(r, g, b) {
return {
values: [r, g, b],
};
}
La sintaxis del constructor es exactamente la misma que la de una funcin normal, lo que significa que puedes usar otras sintaxis, como los parmetros rest:
class Color {
constructor(...values) {
this.values = values;
}
}
const red = new Color(255, 0, 0);
// Crea una instancia con la misma forma que la anterior.
Cada vez que llamas a new, se crea una instancia distinta.
const red = new Color(255, 0, 0);
const anotherRed = new Color(255, 0, 0);
console.log(red === anotherRed); // false
Dentro del constructor de una clase, el valor de this apunta a la instancia recin creada. Puedes asignarle propiedades o leer las que ya existen (sobre todo los mtodos, que veremos a continuacin).
El valor de this se devuelve automticamente como resultado de new. Se recomienda no devolver ningn valor desde el constructor, porque si devuelves un valor no primitivo, se convertir en el valor de la expresin new y el valor de this se descartar. (Puedes leer ms sobre lo que hace new en su descripcin).
class MyClass {
constructor() {
this.myField = "foo";
return {};
}
}
console.log(new MyClass().myField); // undefined
Si una clase solo tiene un constructor, no es muy diferente de una funcin de fbrica createX que simplemente crea objetos normales. Sin embargo, la potencia de las clases est en que pueden usarse como "plantillas" que asignan automticamente mtodos a las instancias.
Por ejemplo, con las instancias de Date, puedes usar varios mtodos para obtener informacin distinta a partir de un nico valor de fecha, como el ao, el mes, el da de la semana, etc. Tambin puedes establecer esos valores con sus equivalentes setX, como setFullYear.
Para nuestra propia clase Color, podemos aadir un mtodo llamado getRed que devuelva el valor rojo del color.
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
}
const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255
Sin mtodos, podras sentir la tentacin de definir la funcin dentro del constructor:
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
this.getRed = function () {
return this.values[0];
};
}
}
Esto tambin funciona. Sin embargo, el problema es que se crea una funcin nueva cada vez que se crea una instancia de Color, aunque todas hagan lo mismo!
console.log(new Color().getRed === new Color().getRed); // false
En cambio, si usas un mtodo, este se comparte entre todas las instancias. Una funcin puede compartirse entre todas las instancias y, aun as, comportarse de forma distinta cuando la llaman instancias diferentes, porque el valor de this es distinto. Si tienes curiosidad por saber dnde se guarda este mtodo: se define en el prototipo de todas las instancias, es decir, en Color.prototype, algo que se explica con ms detalle en Herencia y la cadena de prototipos.
Del mismo modo, podemos crear un mtodo nuevo llamado setRed, que establezca el valor rojo del color.
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
getRed() {
return this.values[0];
}
setRed(value) {
this.values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.setRed(0);
console.log(red.getRed()); // 0; a estas alturas, claro, debera llamarse "black"!
Quiz te preguntes: por qu complicarse usando los mtodos getRed y setRed, si podemos acceder directamente al array values de la instancia?
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
}
const red = new Color(255, 0, 0);
red.values[0] = 0;
console.log(red.values[0]); // 0
En la programacin orientada a objetos hay una filosofa llamada "encapsulacin". Significa que no debes acceder a la implementacin subyacente de un objeto, sino usar mtodos bien abstrados para interactuar con l. Por ejemplo, si de repente decidiramos representar los colores en HSL:
class Color {
constructor(r, g, b) {
// values ahora es un array HSL!
this.values = rgbToHSL([r, g, b]);
}
getRed() {
return hslToRGB(this.values)[0];
}
setRed(value) {
const rgb = hslToRGB(this.values);
rgb[0] = value;
this.values = rgbToHSL(rgb);
}
}
const red = new Color(255, 0, 0);
console.log(red.values[0]); // 0; ya no es 255, porque el valor H del rojo puro es 0
La suposicin del usuario de que values es el valor RGB se viene abajo de repente, y puede hacer que su lgica falle. Por eso, si implementas una clase, querrs ocultar la estructura interna de los datos de tu instancia a sus usuarios, tanto para mantener la API limpia como para evitar que su cdigo falle cuando hagas alguna "refactorizacin inofensiva". En las clases, esto se hace con los campos privados.
Un campo privado es un identificador precedido de # (el smbolo de almohadilla). La almohadilla forma parte del nombre del campo, lo que significa que un campo privado nunca puede tener un conflicto de nombre con un campo o mtodo pblico. Para referirte a un campo privado en cualquier parte de la clase, debes declararlo en el cuerpo de la clase (no puedes crear un elemento privado sobre la marcha). Aparte de esto, un campo privado equivale prcticamente a una propiedad normal.
class Color {
// Declarar: toda instancia de Color tiene un campo privado llamado #values.
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
getRed() {
return this.#values[0];
}
setRed(value) {
this.#values[0] = value;
}
}
const red = new Color(255, 0, 0);
console.log(red.getRed()); // 255
Acceder a campos privados desde fuera de la clase es un error de sintaxis temprano. El lenguaje puede evitarlo porque #privateField es una sintaxis especial, as que puede hacer un anlisis esttico y encontrar todos los usos de campos privados antes incluso de evaluar el cdigo.
console.log(red.#values); // SyntaxError: Private field '#values' must be declared in an enclosing class
Nota: El cdigo que se ejecuta en la consola de Chrome puede acceder a elementos privados desde fuera de la clase. Es una flexibilizacin de la restriccin de sintaxis de JavaScript exclusiva de las herramientas de desarrollo.
Los campos privados de JavaScript son estrictamente privados: si la clase no implementa mtodos que expongan esos campos privados, no hay absolutamente ningn mecanismo para obtenerlos desde fuera de la clase. Esto significa que puedes refactorizar con seguridad los campos privados de tu clase, siempre que el comportamiento de los mtodos expuestos no cambie.
Despus de hacer privado el campo values, podemos aadir ms lgica en los mtodos getRed y setRed, en lugar de que se limiten a pasar el valor. Por ejemplo, podemos aadir una comprobacin en setRed para ver si es un valor R vlido:
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
getRed() {
return this.#values[0];
}
setRed(value) {
if (value < 0 || value > 255) {
throw new RangeError("Invalid R value");
}
this.#values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.setRed(1000); // RangeError: Invalid R value
Si dejamos expuesta la propiedad values, nuestros usuarios pueden saltarse fcilmente esa comprobacin asignando directamente a values[0], y crear colores no vlidos. Pero con una API bien encapsulada, podemos hacer que nuestro cdigo sea ms robusto y evitar errores de lgica ms adelante.
Un mtodo de clase puede leer los campos privados de otras instancias, siempre que pertenezcan a la misma clase.
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
redDifference(anotherColor) {
// No es necesario acceder a #values desde this:
// puedes acceder a los campos privados de otras instancias
// que pertenezcan a la misma clase.
return this.#values[0] - anotherColor.#values[0];
}
}
const red = new Color(255, 0, 0);
const crimson = new Color(220, 20, 60);
red.redDifference(crimson); // 35
Sin embargo, si anotherColor no es una instancia de Color, #values no existir. (Aunque otra clase tenga un campo privado #values con el mismo nombre, no se refiere a lo mismo y no se puede acceder a l aqu). Acceder a un elemento privado que no existe lanza un error, en lugar de devolver undefined como hacen las propiedades normales. Si no sabes si un campo privado existe en un objeto y quieres acceder a l sin usar try/catch para gestionar el error, puedes usar el operador in.
class Color {
#values;
constructor(r, g, b) {
this.#values = [r, g, b];
}
redDifference(anotherColor) {
if (!(#values in anotherColor)) {
throw new TypeError("Color instance expected");
}
return this.#values[0] - anotherColor.#values[0];
}
}
Nota:
Ten en cuenta que # es una sintaxis de identificador especial, y no puedes usar el nombre del campo como si fuera una cadena. "#values" in anotherColor buscara una propiedad llamada literalmente "#values", en lugar de un campo privado.
El uso de elementos privados tiene algunas limitaciones: no se puede declarar el mismo nombre dos veces en una misma clase, y no se pueden eliminar. Ambas cosas provocan errores de sintaxis tempranos.
class BadIdeas {
#firstName;
#firstName; // aqu se produce un error de sintaxis
#lastName;
constructor() {
delete this.#lastName; // tambin es un error de sintaxis
}
}
Los mtodos, los getters y los setters tambin pueden ser privados. Son tiles cuando la clase tiene que hacer algo complejo internamente que ninguna otra parte del cdigo debera poder llamar.
Por ejemplo, imagina que creas elementos personalizados de HTML que deben hacer algo un tanto complicado cuando se hace clic en ellos, se tocan o se activan de otra forma. Adems, esas cosas algo complicadas que ocurren al hacer clic en el elemento deben limitarse a esta clase, porque ninguna otra parte del JavaScript acceder a ellas (ni debera).
class Counter extends HTMLElement {
#xValue = 0;
constructor() {
super();
this.onclick = this.#clicked.bind(this);
}
get #x() {
return this.#xValue;
}
set #x(value) {
this.#xValue = value;
window.requestAnimationFrame(this.#render.bind(this));
}
#clicked() {
this.#x++;
}
#render() {
this.textContent = this.#x.toString();
}
connectedCallback() {
this.#render();
}
}
customElements.define("num-counter", Counter);
En este caso, prcticamente todos los campos y mtodos son privados de la clase. As, presenta al resto del cdigo una interfaz que, en esencia, es igual que la de un elemento HTML integrado. Ninguna otra parte del programa puede afectar a los detalles internos de Counter.
color.getRed() y color.setRed() nos permiten leer y escribir el valor rojo de un color. Si vienes de lenguajes como Java, este patrn te resultar muy familiar. Sin embargo, usar mtodos solo para acceder a una propiedad sigue siendo poco prctico en JavaScript. Los campos de acceso nos permiten manipular algo como si fuera una "propiedad real".
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
get red() {
return this.values[0];
}
set red(value) {
this.values[0] = value;
}
}
const red = new Color(255, 0, 0);
red.red = 0;
console.log(red.red); // 0
Parece que el objeto tiene una propiedad llamada red, pero en realidad esa propiedad no existe en la instancia. Solo hay dos mtodos, pero llevan delante get y set, lo que permite manipularlos como si fueran propiedades.
Si un campo solo tiene un getter pero no un setter, ser en la prctica de solo lectura.
class Color {
constructor(r, g, b) {
this.values = [r, g, b];
}
get red() {
return this.values[0];
}
}
const red = new Color(255, 0, 0);
red.red = 0;
console.log(red.red); // 255
En modo estricto, la lnea red.red = 0 lanzar un error de tipo: "Cannot set property red of #<Color> which has only a getter". En modo no estricto, la asignacin se ignora sin avisar.
Los campos privados tambin tienen su equivalente pblico, que permite que cada instancia tenga una propiedad. Los campos suelen disearse para ser independientes de los parmetros del constructor.
class MyClass {
luckyNumber = Math.random();
}
console.log(new MyClass().luckyNumber); // 0.5
console.log(new MyClass().luckyNumber); // 0.3
Los campos pblicos equivalen casi a asignar una propiedad a this. Por ejemplo, el ejemplo anterior tambin podra convertirse en:
class MyClass {
constructor() {
this.luckyNumber = Math.random();
}
}
En el ejemplo de Date, tambin vimos el mtodo Date.now(), que devuelve la fecha actual. Este mtodo no pertenece a ninguna instancia de fecha, sino a la propia clase. Sin embargo, se coloca en la clase Date en lugar de exponerse como una funcin global DateNow(), porque resulta til sobre todo al trabajar con instancias de fecha.
Nota:
Poner delante de los mtodos de utilidad aquello con lo que trabajan se llama "espacio de nombres" y se considera una buena prctica. Por ejemplo, adems del mtodo ms antiguo y sin prefijo parseInt(), JavaScript aadi ms tarde el mtodo con prefijo Number.parseInt() para indicar que sirve para trabajar con nmeros.
Las propiedades estticas son un grupo de caractersticas de las clases que se definen en la propia clase, y no en las instancias individuales. Estas caractersticas son:
Todo ello tiene tambin su equivalente privado. Por ejemplo, para nuestra clase Color, podemos crear un mtodo esttico que compruebe si un tro de valores dado es un valor RGB vlido:
class Color {
static isValid(r, g, b) {
return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
}
}
Color.isValid(255, 0, 0); // true
Color.isValid(1000, 0, 0); // false
Las propiedades estticas son muy parecidas a sus equivalentes de instancia, salvo que:
static, yconsole.log(new Color(0, 0, 0).isValid); // undefined
Tambin hay una construccin especial llamada bloque de inicializacin esttica, que es un bloque de cdigo que se ejecuta cuando la clase se carga por primera vez.
class MyClass {
static {
MyClass.myStaticProperty = "foo";
}
}
console.log(MyClass.myStaticProperty); // 'foo'
Los bloques de inicializacin esttica equivalen casi a ejecutar inmediatamente algo de cdigo despus de declarar una clase. La nica diferencia es que tienen acceso a los elementos privados estticos.
Una caracterstica clave que aportan las clases (adems de una encapsulacin prctica con los campos privados) es la herencia, que significa que un objeto puede "tomar prestada" gran parte del comportamiento de otro objeto, mientras sobrescribe o mejora ciertas partes con su propia lgica.
Por ejemplo, supongamos que nuestra clase Color ahora tiene que admitir transparencia. Podramos sentir la tentacin de aadir un campo nuevo que indique su transparencia:
class Color {
#values;
constructor(r, g, b, a = 1) {
this.#values = [r, g, b, a];
}
get alpha() {
return this.#values[3];
}
set alpha(value) {
if (value < 0 || value > 1) {
throw new RangeError("Alpha value must be between 0 and 1");
}
this.#values[3] = value;
}
}
Sin embargo, esto significa que todas las instancias, incluso la inmensa mayora que no son transparentes (las que tienen un valor alfa de 1), tendrn que tener el valor alfa adicional, lo que no es muy elegante. Adems, si las funciones siguen creciendo, nuestra clase Color se volver muy pesada y difcil de mantener.
En su lugar, en la programacin orientada a objetos crearamos una clase derivada. La clase derivada tiene acceso a todas las propiedades pblicas de la clase padre. En JavaScript, las clases derivadas se declaran con una clusula extends, que indica la clase de la que heredan.
class ColorWithAlpha extends Color {
#alpha;
constructor(r, g, b, a) {
super(r, g, b);
this.#alpha = a;
}
get alpha() {
return this.#alpha;
}
set alpha(value) {
if (value < 0 || value > 1) {
throw new RangeError("Alpha value must be between 0 and 1");
}
this.#alpha = value;
}
}
Hay algunas cosas que llaman la atencin enseguida. La primera es que en el constructor llamamos a super(r, g, b). El lenguaje exige llamar a super() antes de acceder a this. La llamada a super() llama al constructor de la clase padre para inicializar this; aqu equivale ms o menos a this = new Color(r, g, b). Puedes tener cdigo antes de super(), pero no puedes acceder a this antes de super(): el lenguaje impide que accedas al this sin inicializar.
Una vez que la clase padre ha terminado de modificar this, la clase derivada puede aplicar su propia lgica. Aqu aadimos un campo privado llamado #alpha y tambin un par de getter/setter para interactuar con l.
Una clase derivada hereda todos los mtodos de su clase padre. Por ejemplo, fjate en el accesor get red() que aadimos a Color en la seccin Campos de acceso: aunque no hemos declarado ninguno en ColorWithAlpha, podemos acceder a red porque ese comportamiento lo especifica la clase padre:
const color = new ColorWithAlpha(255, 0, 0, 0.5);
console.log(color.red); // 255
Las clases derivadas tambin pueden sobrescribir mtodos de la clase padre. Por ejemplo, todas las clases heredan implcitamente de la clase Object, que define algunos mtodos bsicos como toString(). Sin embargo, el mtodo toString() base es famoso por ser intil, porque en la mayora de los casos imprime [object Object]:
console.log(red.toString()); // [object Object]
En cambio, nuestra clase puede sobrescribirlo para que imprima los valores RGB del color:
class Color {
#values;
//
toString() {
return this.#values.join(", ");
}
}
console.log(new Color(255, 0, 0).toString()); // '255, 0, 0'
Dentro de las clases derivadas, puedes acceder a los mtodos de la clase padre con super. Esto te permite crear mtodos que los mejoran y evitar duplicar cdigo.
class ColorWithAlpha extends Color {
#alpha;
//
toString() {
// Llamar a toString() de la clase padre y partir del valor devuelto
return `${super.toString()}, ${this.#alpha}`;
}
}
console.log(new ColorWithAlpha(255, 0, 0, 0.5).toString()); // '255, 0, 0, 0.5'
Cuando usas extends, los mtodos estticos tambin se heredan, as que tambin puedes sobrescribirlos o mejorarlos.
class ColorWithAlpha extends Color {
//
static isValid(r, g, b, a) {
// Llamar a isValid() de la clase padre y partir del valor devuelto
return super.isValid(r, g, b) && a >= 0 && a <= 1;
}
}
console.log(ColorWithAlpha.isValid(255, 0, 0, -1)); // false
Las clases derivadas no tienen acceso a los campos privados de la clase padre: este es otro aspecto clave de que los campos privados de JavaScript sean "estrictamente privados". Los campos privados estn limitados al propio cuerpo de la clase y no dan acceso a ningn cdigo externo.
class ColorWithAlpha extends Color {
log() {
console.log(this.#values); // SyntaxError: Private field '#values' must be declared in an enclosing class
}
}
Una clase solo puede heredar de una clase. Esto evita problemas de la herencia mltiple, como el problema del diamante. Sin embargo, debido a la naturaleza dinmica de JavaScript, sigue siendo posible lograr el efecto de la herencia mltiple mediante la composicin de clases y los mixins.
Las instancias de las clases derivadas tambin son instancias de la clase base.
const color = new ColorWithAlpha(255, 0, 0, 0.5);
console.log(color instanceof Color); // true
console.log(color instanceof ColorWithAlpha); // true
Hasta ahora, la gua ha sido pragmtica: nos hemos centrado en cmo se pueden usar las clases, pero queda una pregunta sin responder: por qu usar una clase? La respuesta es: depende.
Las clases introducen un paradigma, es decir, una forma de organizar el cdigo. Las clases son la base de la programacin orientada a objetos, que se basa en conceptos como la herencia y el polimorfismo (sobre todo el polimorfismo de subtipos). Sin embargo, muchas personas se oponen filosficamente a ciertas prcticas de la POO y, por eso, no usan clases.
Por ejemplo, algo que hace que los objetos Date tengan mala fama es que son mutables.
function incrementDay(date) {
return new Date(date.setDate(date.getDate() + 1));
}
const date = new Date(); // 2019-06-19
const newDay = incrementDay(date);
console.log(newDay); // 2019-06-20
// La fecha antigua tambin se modifica?!
console.log(date); // 2019-06-20
La mutabilidad y el estado interno son aspectos importantes de la programacin orientada a objetos, pero a menudo hacen que sea difcil razonar sobre el cdigo, porque cualquier operacin aparentemente inocente puede tener efectos secundarios inesperados y cambiar el comportamiento de otras partes del programa.
Para reutilizar cdigo, solemos recurrir a heredar de clases, lo que puede crear grandes jerarquas de patrones de herencia.
[Un rbol de herencia tpico de la POO, con cinco clases y tres niveles]
Sin embargo, a menudo es difcil describir la herencia de forma limpia cuando una clase solo puede heredar de otra. A menudo queremos el comportamiento de varias clases. En Java, esto se hace con interfaces; en JavaScript, puede hacerse con mixins. Pero, al final, sigue sin ser muy cmodo.
Por el lado bueno, las clases son una forma muy potente de organizar el cdigo a un nivel superior. Por ejemplo, sin la clase Color, quiz tendramos que crear una docena de funciones de utilidad:
function isRed(color) {
return color.red === 255;
}
function isValidColor(color) {
return (
color.red >= 0 &&
color.red <= 255 &&
color.green >= 0 &&
color.green <= 255 &&
color.blue >= 0 &&
color.blue <= 255
);
}
//
Pero con las clases, podemos agruparlas todas bajo el espacio de nombres Color, lo que mejora la legibilidad. Adems, la introduccin de los campos privados nos permite ocultar ciertos datos a los usuarios posteriores, creando una API limpia.
En general, deberas plantearte usar clases cuando quieras crear objetos que guarden sus propios datos internos y expongan mucho comportamiento. Toma como ejemplo las clases integradas de JavaScript:
Map y Set guardan una coleccin de elementos y te permiten acceder a ellos por su clave con get(), set(), has(), etc.Date guarda una fecha como una marca de tiempo Unix (un nmero) y te permite dar formato a los componentes individuales de la fecha, actualizarlos y leerlos.Error guarda informacin sobre una excepcin concreta, incluidos el mensaje de error, la traza de la pila, la causa, etc. Es una de las pocas clases que vienen con una estructura de herencia rica: hay varias clases integradas, como TypeError y ReferenceError, que heredan de Error. En el caso de los errores, esta herencia permite afinar la semntica de los errores: cada clase de error representa un tipo concreto de error, que se puede comprobar fcilmente con instanceof.JavaScript ofrece el mecanismo para organizar el cdigo de una forma cannica orientada a objetos, pero si se usa y cmo se usa queda por completo a criterio de cada programador.
Esta pgina se modific por ltima vez el 30 sept 2026 por colaboradores de MDN.
Partes de este contenido son 19982026 de colaboradores individuales de mozilla.org. Contenido disponible bajo una licencia Creative Commons.
| Web Proxy Viewer | New URL | Original Page |