Ampliación de clases incorporadas
Las clases integradas en JavaScript, como Array, Map y otras, pueden extenderse para añadir funcionalidades personalizadas.
Por ejemplo, podemos crear una clase PowerArray que hereda del Array nativo:
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
console.log(arr.isEmpty()); // false
let filteredArr = arr.filter(item => item >= 10);
console.log(filteredArr); // [10, 50]
console.log(filteredArr.isEmpty()); // false
Es interesante notar que métodos nativos como filter
, map
y otros devuelven nuevos objetos del mismo tipo que el objeto heredado, en este caso, PowerArray. Esto se logra internamente utilizando la propiedad constructor
del objeto:
console.log(arr.constructor === PowerArray); // true
Cuando se llama a arr.filter()
, internamente se utiliza arr.constructor
para crear la nueva matriz de resultados, no simplemente Array. Esto permite que podamos seguir utilizando métodos personalizados de PowerArray en el resultado.
Podemos personalizar este comportamiento aún más añadiendo un getter estático especial Symbol.species
a la clase. Este getter especifica qué constructor JavaScript debe usar internamente para crear nuevas entidades en métodos como map
, filter
, entre otros.
Si deseamos que los métodos incorporados como map
o filter
devuelvan matrices regulares en lugar de PowerArray, podemos configurar Symbol.species
para devolver Array:
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
static get [Symbol.species]() {
return Array;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
console.log(arr.isEmpty()); // false
// filter ahora devuelve una matriz estándar utilizando Array como constructor
let filteredArr = arr.filter(item => item >= 10);
// filteredArr ya no es PowerArray, sino Array
console.log(filteredArr.isEmpty()); // Error: filteredArr.isEmpty no es una función
Como se puede ver, ahora filter
devuelve una instancia de Array en lugar de PowerArray. Esto asegura que la funcionalidad extendida no se herede en el resultado.
Otras colecciones como Map y Set funcionan de manera similar, utilizando también Symbol.species
para definir su comportamiento de construcción de instancias.
Herencia estática en objetos integrados
A diferencia de las clases personalizadas, los objetos integrados en JavaScript no heredan métodos estáticos entre sí. Por ejemplo, Array y Date, a pesar de que ambos heredan de Object, no comparten métodos estáticos como keys()
:
// Array y Date no heredan métodos estáticos entre sí
console.log(Array.keys); // undefined
console.log(Date.keys); // undefined
Aunque Array y Date comparten Object.prototype como su prototipo base, sus objetos no comparten métodos estáticos directamente entre sí. Cada uno tiene su propia implementación independiente.
Esta diferencia en la herencia de métodos estáticos es crucial cuando se trabaja con objetos integrados en comparación con la herencia estándar utilizando extends
.