扩展内置类
javascript内置类比如数组,哈希和其他内置类等都是可扩展的。
比如,这里的PowerArray
就是从原生Array
继承而来的
// 给原生数组添加额外方法(还可以添加更多)
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
请注意一个有趣的现象。内置方法,比如filter
,map
和其他方法 - 都返回了继承类的新实例。这是由它们的constructor
属性所导致的。
在在上述例子中就是,
arr.constructor === PowerArray;
所以调用arr.filter()
时, 会在方法内部为返回结果创建新数组,这个新数组确切地说是使用new PowerArray
的方式而不是new Array
的方式创建的。这很好,因为我们可以在返回结果上继续使用PowerArray
的方法。
甚至,我们可以定制这一行为。
我们可以为这个类添加一个特殊的静态取值函数Symbol.species
。它会返回 JavaScript 默默地用来在map
, filter
等方法中创建新实例的构造函数。
假如我们想要内置方法比如map
,filter
中返回常规的数组,那么我们可以在Symbol.species
返回内置数组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);
alert(arr.isEmpty()); // false
// filter方法使用 arr.constructor[Symbol.species] 作为构造函数来创建返回的新数组
let filteredArr = arr.filter(item => item >= 10);
// filteredArray 的构造函数不是PowerArray, 而是Array
alert(filteredArr.isEmpty()); // Error: filteredArr.isEmpty is not a function
正如你所看到的,现在 .filter
返回了 Array
类型。因此继承的方法并没有进一步的传递下来。
内置类中不存在静态继承
内置对象都有自己的静态方法,比如Object.keys
,Array.isArray
等。
众所周知,内置类之间会彼此继承。比如,Array
继承自 Object
。
一般来说,当一个类继承自另一个时,静态和常规方法都会被继承。
所以,当Rabbit extends Animal
,其结果是:
- 静态方法
Rabbit.methods
继承自Animal.methods
,因为Rabbit.[[prototype]] = Animal
。 - 实例中的常规方法
new Rabbit().methods
也会继承,因为Rabbit.prototype.[[prototype]] = Animal.prototype
。
这在 静态属性和静态方法 章节中已经仔细解释过了。
但是,内置类除外。它们并不能在彼此之间继承静态属性和方法。
比如,Array
和Date
对象都继承自Object
, 因此它们的实例拥有Object.prototype
上的方法。但是Array.[[prototype]]
并不指向Object
。所以,存在Object.keys()
方法,却并不存在Array.keys()
和Date.keys()
方法。
下图展示了Date
和Object
的关系
注意,Date
和Object
之间并没有联系。Object
和Date
两个类都是独立存在的。
仅仅是,Date.prototype
继承自Object.prototype
。