Array、Mapなどの組み込みクラスも拡張可能です。
例えば、ここでは`PowerArray`がネイティブの`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
非常に興味深い点に注意してください。`filter`、`map`などの組み込みメソッドは、継承された型`PowerArray`の新しいオブジェクトを返します。それらの内部実装では、オブジェクトの`constructor`プロパティが使用されます。
上記の例では、
arr.constructor === PowerArray
`arr.filter()`が呼び出されると、内部的に基本的な`Array`ではなく、`arr.constructor`を使用して結果の新しい配列が作成されます。これは実際には非常に優れており、結果に対して`PowerArray`メソッドを引き続き使用できます。
さらに、その動作をカスタマイズできます。
クラスに特別な静的ゲッター`Symbol.species`を追加できます。それが存在する場合、JavaScriptが`map`、`filter`などで新しいエンティティを作成するために内部的に使用するコンストラクタを返す必要があります。
`map`や`filter`などの組み込みメソッドが通常の配列を返すようにしたい場合は、ここでのように`Symbol.species`で`Array`を返すことができます。
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
ご覧のとおり、これで`.filter`は`Array`を返します。そのため、拡張機能はそれ以上渡されません。
`Map`や`Set`などの他のコレクションも同様に動作します。これらも`Symbol.species`を使用します。
組み込みオブジェクトにおける静的継承の欠如
組み込みオブジェクトには、`Object.keys`、`Array.isArray`など独自の静的メソッドがあります。
すでに知っているように、ネイティブクラスはお互いを拡張します。例えば、`Array`は`Object`を拡張します。
通常、あるクラスが別のクラスを拡張する場合、静的メソッドと非静的メソッドの両方が継承されます。これは記事静的プロパティとメソッドで詳しく説明されています。
しかし、組み込みクラスは例外です。互いに静的メソッドを継承しません。
たとえば、`Array`と`Date`の両方が`Object`を継承するため、それらのインスタンスには`Object.prototype`のメソッドがあります。しかし、`Array.[[Prototype]]`は`Object`を参照しないため、例えば`Array.keys()`(または`Date.keys()`)という静的メソッドはありません。
`Date`と`Object`の構造図を以下に示します。
ご覧のとおり、`Date`と`Object`の間にはリンクがありません。これらは独立しており、`Date.prototype`だけが`Object.prototype`を継承します。
これは、`extends`で得られるものと比較して、組み込みオブジェクト間の継承における重要な違いです。
コメント
<code>
タグを使用し、数行の場合は<pre>
タグで囲み、10行を超える場合はサンドボックス(plnkr、jsbin、codepen…)を使用してください。