Коротка відповідь
juniorArray.prototype.push = function(){/*…*/}; – це можливе, але призводить до колізій і небажаних побічних ефектів. Розширювати прототипи слід лише для власних класів або коли API чітко дозволяє, наприклад через Symbol.species. Якщо треба розширити стандартний прототип, використовуйте Object.defineProperty з флагом writable: false і перевіряйте наявність властивості перед записом.
Повне пояснення
Що це і навіщо
Розширення прототипу (prototype augmentation) – додавання нових властивостей або методів до вже існуючого об’єкта прототипу, наприклад Array.prototype. Це дозволяє всім екземплярам типу Array отримати новий метод.
Ключові принципи/терміни
- Prototype chain – ланцюжок прототипів, через який здійснюється пошук властивостей.
- Property descriptor – опис атрибутів (writable, enumerable, configurable).
- Symbol.species – спеціальний символ, що визначає тип об’єкта, який повертається у методах, що створюють нові інстанції.
Як це працює
Коли викликається метод, JavaScript спочатку шукає його у власних властивостях об’єкта. Якщо не знайдено, переходить до прототипу. Додавши новий метод у Array.prototype, ви змінюєте поведінку всіх масивів.
Практика і реалізація
// Небезпечне розширення
Array.prototype.sum = function(){ return this.reduce((a,b)=>a+b,0); };
// Безпечніше: перевірка наявності
if (!Array.prototype.sum) {
Object.defineProperty(Array.prototype, 'sum', {
value: function(){ return this.reduce((a,b)=>a+b,0); },
writable: false,
configurable: true
});
}
Тестування
import { test, expect } from 'vitest';
test('Array.prototype.sum', () => {
const arr = [1,2,3];
expect(arr.sum()).toBe(6);
});
Дизайн‑рішення і моделі даних
- Якщо розширюєте власний клас, використовуйте
class MyArray extends Array {}. - Для глобальних змін застосовуйте polyfill лише при відсутності нативної реалізації.
Безпека і приватність
- Не змінюйте властивості, що вже існують (наприклад
Array.prototype.push). - Уникайте колізій і перекриття стандартних методів.
Особливості в контексті JS Core
- Array і Object – найчастіше розширювані, але їх API не передбачає додавання нових методів.
- Map, Set – також можна розширювати, але це рідко практикується.
Оптимізація і типові помилки
- Колізія – новий метод перекриває існуючий.
- Проблеми із serialisation – нові властивості не зберігаються у
JSON.stringify. - Проблеми із спадковістю – нові методи можуть бути недоступні у підкласах.
Диагностика і відладка
- Проблема: новий метод не працює.
- Причина: властивість вже існує, або вона не writable.
- Дія: перевірте
Object.getOwnPropertyDescriptor(Array.prototype, 'sum').
Альтернативи
- Створіть окремий клас‑обгортку.
- Використовуйте функції‑утиліти замість розширення прототипу.
Крайні випадки і часті помилки
- Додавання
Array.prototype.push→ перезапис стандартного методу. - Використання
Object.definePropertyбезconfigurable: true→ неможливе видалення. - Додавання властивості без
enumerable: false→ вона з’явиться у цикліfor…in. - Перевизначення
toString→ зміна поведінки в логуванні. - Додавання
Array.prototype[Symbol.iterator]→ змінює ітерацію. - Додавання
Object.prototype.__proto__→ створює рекурсію. - Додавання
Object.prototype.constructor→ змінює конструктор всіх об’єктів. - Додавання
Array.prototype.length→ порушує внутрішню структуру масиву.
Cheatsheet
- Не розширюйте:
Array,Object,Functionбез крайньої потреби. - Якщо треба: перевіряйте наявність, використовуйте
Object.definePropertyзwritable: false. - Підтримка: polyfills лише для відсутніх методів, не для нових.
- Питання: Чому
Array.prototype.pushне можна змінювати? – бо це стандартний метод, його зміна порушує API. - Відповідь: Тому що це призведе до непередбачуваної поведінки в сторонньому коді.