Кратко
JavaScript позволяет наследоваться от встроенных классов: class MyArray extends Array. Это полезно для кастомных коллекций. Ограничения:Species pattern, не все built-ins расширяемы одинаково. В обычном коде расширение built-ins встречается редко.
Подробно
Расширение Array: class MyArray extends Array { get first() { return this[0]; } get last() { return this[this.length - 1]; } }
const arr = new MyArray(1, 2, 3); console.log(arr.first); // 1 console.log(arr.last); // 3 console.log(arr instanceof MyArray); // true
// Методы возвращают MyArray: const filtered = arr.filter(x => x > 1); console.log(filtered instanceof MyArray); // true console.log(filtered.first); // 2
Species pattern: class MyError extends Error { constructor(message) { super(message); this.name = 'MyError'; } }
// По умолчанию: MyError[Symbol.species] = MyError // Это значит: методы Error будут возвращать MyError
Расширение Map: class DurationMap extends Map { setWithTimeout(key, value, ms) { this.set(key, value); setTimeout(() => this.delete(key), ms); } }
Ограничения:
- Symbol.species может изменять поведение методов
- Не все built-ins имеют одинаковую поддержку
- Некоторые встроенные методы могут не работать корректно
Ментальная модель
Расширение built-ins — как создать кастомный инструмент на основе стандартного. Например: MyArray — это Array с дополнительными кнопками (first, last). Стандартные функции (filter, map) работают и возвращают MyArray, потому что JavaScript использует Species pattern.
Примеры
**Самый простой:**
class CountedArray extends Array {
get count() {
return this.length;
}
}
const arr = new CountedArray(1, 2, 3);
console.log(arr.count); // 3Простейшее расширение Array с собственным методом.
**Простой:**
class TaggedError extends Error {
constructor(tag, message) {
super(message);
this.tag = tag;
this.name = 'TaggedError';
}
}
const err = new TaggedError('AUTH', 'Unauthorized');
console.log(err.tag); // 'AUTH'
console.log(err instanceof Error); // trueРасширение Error для кастомных ошибок.
**Реальный пример:**
class PriorityQueue extends Array {
enqueue(item, priority) {
this.push({ item, priority });
this.sort((a, b) => a.priority - b.priority);
}
dequeue() {
return this.shift()?.item;
}
}
const pq = new PriorityQueue();
pq.enqueue('low', 3);
pq.enqueue('high', 1);
pq.enqueue('medium', 2);
console.log(pq.dequeue()); // 'high'
console.log(pq.dequeue()); // 'medium'Расширение Array для Priority Queue.
Частые ошибки
**Неправильно:** Расширять Array без понимания Species
**Почему:** Методы Array (filter, map) возвращают экземпляр родителя — это может нарушить ожидания.
**Правильно:** Понимайте Symbol.species и его влияние на возвращаемые типы.
**Неправильно:** Думать, что расширение built-ins — обычный паттерн
**Почему:** В большинстве проектов достаточно обычных классов и композиции.
**Правильно:** Расширение built-ins — нишевый паттерн. Используйте только когда действительно нужно.
Важно запомнить
- class MyArray extends Array — допустимый синтаксис
- Symbol.species определяет тип возвращаемых методов
- Методы возвращают экземпляр дочернего класса
- Расширение built-ins — нишевый паттерн
- Используйте композицию вместо наследования, когда возможно
Связь
Назад: Вы знаете классы (JI8) и наследование (JI9). Этот урок расширяет встроенные классы.
Вперёд: Следующий урок (JV14) — миксины — переиспользуемое поведение через композицию.