Las clases integradas como Array, Map y otras también son extensibles.
Por ejemplo, aquí PowerArray
hereda del Array
nativo:
// se agrega un método más (puedes hacer más)
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // falso
let filteredArr = arr.filter(item => item >= 10);
alert(filteredArr); // 10, 50
alert(filteredArr.isEmpty()); // falso
Tenga en cuenta una cosa muy interesante. Métodos nativos como filter
, map
, y otros, devuelven los nuevos objetos exactamente del tipo heredado PowerArray
. Su implementación interna utiliza la propiedad constructor
del objeto para eso.
En el ejemplo anterior,
arr.constructor === PowerArray
Cuando se llama a arr.filter()
, crea internamente la nueva matriz de resultados usando exactamente arr.constructor
, no el básico Array
. En realidad, eso es muy bueno, porque podemos seguir usando métodos PowerArray
más adelante en el resultado.
Aún más, podemos personalizar ese comportamiento.
Podemos agregar un getter
estático especial Symbol.species
a la clase. Si existe, debería devolver el constructor que JavaScript usará internamente para crear nuevas entidades en map
, filter
y así sucesivamente.
Si queremos que los métodos incorporados como map
o filter
devuelvan matrices regulares, podemos devolver Array
en Symbol.species
, como aquí:
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
// los métodos incorporados usarán esto como el constructor
static get [Symbol.species]() {
return Array;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // falso
// filter crea una nueva matriz usando arr.constructor[Symbol.species] como constructor
let filteredArr = arr.filter(item => item >= 10);
// filterArr no es PowerArray, sino Array
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty no es una función
Como puede ver, ahora .filter
devuelve un Array
. Por lo tanto, la funcionalidad extendida ya no se pasa.
Otras colecciones, como Map
y Set
, funcionan igual. También usan Symbol.species
.
Sin herencia estática en incorporados
Los objetos nativos tienen sus propios métodos estáticos, por ejemplo, Object.keys
, Array.isArray
, etc.
Como ya sabemos, las clases nativas se extienden entre sí. Por ejemplo, Array
extiende Object
.
Normalmente, cuando una clase extiende a otra, se heredan los métodos estáticos y no estáticos. Eso se explicó a fondo en el artículo Propiedades y métodos estáticos..
Pero las clases nativas son una excepción. No heredan estáticos el uno del otro.
Por ejemplo, tanto Array
como Date
heredan de Object
, por lo que sus instancias tienen métodos de Object.prototype
. Pero Array.[[Prototype]]
no hace referencia a Object
, por lo que no existe, por ejemplo, el método estático Array.keys()
(o Date.keys()
).
Imagen de la estructura para Date
y Object
:
Como puede ver, no hay un vínculo entre Date
y Object
. Son independientes, solo Date.prototype
hereda de Object.prototype
.
Esa es una diferencia importante de herencia entre los objetos integrados en comparación con lo que obtenemos con 'extends`.