Встроенные классы, такие как Array, Map и другие, также являются расширяемыми.
Например, здесь PowerArray
наследуется от собственного Array
:
// добавляем к нему еще один метод (можно больше) класс PowerArray расширяет массив { isEmpty() { вернуть this.length === 0; } } пусть arr = новый PowerArray(1, 2, 5, 10, 50); оповещение(arr.isEmpty()); // ЛОЖЬ let filteredArr = arr.filter(item => item >= 10); оповещение (filteredArr); // 10, 50 оповещение(filteredArr.isEmpty()); // ЛОЖЬ
Обратите внимание на очень интересную вещь. Встроенные методы вроде filter
, map
и другие — возвращают новые объекты именно унаследованного типа PowerArray
. Их внутренняя реализация использует для этого свойство объекта constructor
.
В приведенном выше примере
arr.constructor === PowerArray
Когда вызывается arr.filter()
, он внутренне создает новый массив результатов, используя именно arr.constructor
, а не базовый Array
. На самом деле это очень здорово, потому что мы можем продолжать использовать методы PowerArray
для получения результата.
Более того, мы можем настроить это поведение.
Мы можем добавить в класс специальный статический геттер Symbol.species
. Если он существует, он должен вернуть конструктор, который JavaScript будет использовать внутри себя для создания новых объектов в map
, filter
и т. д.
Если мы хотим, чтобы встроенные методы, такие как map
или filter
, возвращали обычные массивы, мы можем вернуть Array
в Symbol.species
, как здесь:
класс PowerArray расширяет массив { isEmpty() { вернуть this.length === 0; } // встроенные методы будут использовать это как конструктор статический get [Symbol.species]() { вернуть массив; } } пусть arr = новый PowerArray(1, 2, 5, 10, 50); оповещение(arr.isEmpty()); // ЛОЖЬ // фильтр создает новый массив, используя arr.constructor[Symbol.species] в качестве конструктора let filteredArr = arr.filter(item => item >= 10); // filteredArr — это не PowerArray, а Array оповещение(filteredArr.isEmpty()); // Ошибка: filteredArr.isEmpty не является функцией
Как видите, теперь .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
.