Built-in classes like Array, Map and others are extendable also.
For instance, here PowerArray
inherits from the native Array
:
// add one more method to it (can do more)
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
let filteredArr = arr.filter(item => item >= 10);
alert(filteredArr); // 10, 50
alert(filteredArr.isEmpty()); // false
Please note a very interesting thing. Built-in methods like filter
, map
and others – return new objects of exactly the inherited type. They rely on the constructor
property to do so.
In the example above,
arr.constructor === PowerArray
So when arr.filter()
is called, it internally creates the new array of results using exactly new PowerArray
, not basic Array
. That’s actually very cool, because we can keep using PowerArray
methods further on the result.
Even more, we can customize that behavior.
We can add a special static getter Symbol.species
to the class. If exists, it should return the constructor that JavaScript will use internally to create new entities in map
, filter
and so on.
If we’d like built-in methods like map
, filter
will return regular arrays, we can return Array
in Symbol.species
, like here:
class PowerArray extends Array {
isEmpty() {
return this.length === 0;
}
// built-in methods will use this as the constructor
static get [Symbol.species]() {
return Array;
}
}
let arr = new PowerArray(1, 2, 5, 10, 50);
alert(arr.isEmpty()); // false
// filter creates new array using arr.constructor[Symbol.species] as constructor
let filteredArr = arr.filter(item => item >= 10);
// filteredArr is not PowerArray, but Array
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
As you can see, now .filter
returns Array
. So the extended functionality is not passed any further.
No static inheritance in built-ins
Built-in objects have their own static methods, for instance Object.keys
, Array.isArray
etc.
As we already know, native classes extend each other. For instance, Array
extends Object
.
Normally, when one class extends another, both static and non-static methods are inherited.
So, if Rabbit extends Animal
, then:
Rabbit.methods
are callable forAnimal.methods
, becauseRabbit.[[Prototype]] = Animal
.new Rabbit().methods
are also available, becauseRabbit.prototype.[[Prototype]] = Animal.prototype
.
That’s thoroughly explained in the chapter Static properties and methods.
But built-in classes are an exception. They don’t inherit statics (1)
from each other.
For example, both Array
and Date
inherit from Object
, so their instances have methods from Object.prototype
. But Array.[[Prototype]]
does not point to Object
. So there’s Object.keys()
, but not Array.keys()
and Date.keys()
.
Here’s the picture structure for Date
and Object
:
Note, there’s no link between Date
and Object
. Both Object
and Date
exist independently. Date.prototype
inherits from Object.prototype
, but that’s all.