W3docs

Встроенные прототипы JavaScript

Узнайте, как работают встроенные прототипы JavaScript: Object.prototype в корне цепочки, прототипы Array, Function и Number, заимствование методов через call и apply.

Изучаем встроенные прототипы

Встроенные прототипы в JavaScript — это объекты, которые такие конструкторы, как Array, Object, String, Number и Function, используют для того, чтобы предоставлять методы и свойства каждому создаваемому ими значению. Когда вы вызываете [1, 2, 3].map(...) или "hi".toUpperCase(), метод, который вы вызываете, не хранится в самом массиве или строке — он живёт в Array.prototype или String.prototype и находится при обходе цепочки прототипов.

На этой странице объясняется, где эти прототипы располагаются в цепочке, как их инспектировать, как заимствовать их методы для объектов, которые не являются настоящими массивами, и почему постоянное расширение встроенных прототипов не рекомендуется. Если вы только знакомитесь с тем, как работает сама цепочка, сначала прочитайте прототипное наследование.

Роль встроенных прототипов

Встроенные прототипы занимают центральное место в JavaScript: именно благодаря им любое литеральное значение сразу же снабжено полезными методами — без того, чтобы вы что-либо определяли. Понимание того, как они вписываются в цепочку прототипов, позволяет разбираться в сообщениях об ошибках, повторно использовать встроенные методы в неожиданных местах и избегать скрытых ошибок.

Object.prototype: корень цепочки

Почти каждый создаваемый вами объект в конечном счёте наследует от Object.prototype, который располагается в вершине цепочки. Именно оттуда берутся такие методы, как toString, hasOwnProperty и valueOf. Когда поиск не находит свойство ни на самом объекте, ни на каком-либо прототипе по пути, цепочка завершается на Object.prototype, а следующая ссылка равна null.

javascript— editable

Прототипы Array, Function и Number

Встроенные типы добавляют собственный прототип поверх Object.prototype. Например, массив наследует от Array.prototype (который предоставляет map, filter, push и т.д.), а Array.prototype, в свою очередь, наследует от Object.prototype. Та же схема применима к функциям и числам.

javascript— editable

Заимствование методов через call и apply

Поскольку встроенные методы живут в прототипах, их можно заимствовать и вызывать для любого совместимого значения с помощью call или apply. Классический пример — использование методов Array.prototype на массивоподобных объектах (таких как arguments или строка), у которых нет этих методов.

javascript— editable

Расширение встроенных прототипов

Хотя JavaScript позволяет расширять встроенные прототипы, эта практика в целом не рекомендуется в глобальной области видимости из-за возможных конфликтов в крупных кодовых базах или со сторонними скриптами. Есть несколько конкретных причин избегать этого:

  • Коллизии имён. Если два скрипта добавляют метод с одинаковым именем (или будущая версия ECMAScript стандартизирует это имя с иным поведением), один из них молча перезаписывает другой.
  • Перечисляемость. Метод, добавленный обычным присваиванием, является перечисляемым, поэтому он будет появляться в циклах for...in для каждого объекта данного типа — если только не использовать защиту через hasOwnProperty. Это частый источник ошибок.
  • Глобальные побочные эффекты. Изменение затрагивает каждое значение данного типа во всей программе, включая код, который вы не писали.

Фрагмент ниже демонстрирует ловушку с for...in. Обычное присваивание «утекает» в цикл; использование Object.defineProperty для создания неперечисляемого метода этого не делает.

javascript— editable

Понимание этой возможности всё равно полезно — для распознавания потенциальных проблем, чтения полифиллов и изучения продвинутых паттернов в контролируемых средах.

Практические примеры работы со встроенными прототипами

Манипуляции с массивами через Array.prototype

Оцените мощь Array.prototype, который предлагает такие методы, как map, filter и reduce. Эти методы обеспечивают элегантные решения для преобразования данных, хранящихся в массивах, и работы с ними. Мы можем добавлять новые методы, манипулируя Array.prototype.


javascript— editable

В этом примере мы определяем новый метод mapToSquare на прототипе, который использует встроенный метод map, чтобы вернуть квадрат каждого числа.

Расширение строк через String.prototype

String.prototype — ещё одно богатое хранилище методов: toLowerCase, toUpperCase, includes и другие, облегчающие операции со строками и их анализ.


javascript— editable

В этом примере мы определяем removeSpace на прототипе, используя split, filter и join для удаления пробелов.

Пользовательские расширения встроенных прототипов

Хотя осторожность необходима, добавление пользовательских методов в встроенные прототипы может продемонстрировать гибкость JavaScript. Вот как можно расширить Array.prototype, добавив метод для вычисления суммы элементов массива:


javascript— editable

Этот пользовательский метод sum добавляет новое измерение к прототипу Array, демонстрируя как потенциал, так и риски расширения встроенных прототипов.

Лучшие практики работы со встроенными прототипами

Хотя мощь встроенных прототипов неоспорима, вот несколько лучших практик, которые помогут сохранить ваш код надёжным и свободным от конфликтов:

  • Избегайте расширения встроенных прототипов: если это не является абсолютной необходимостью, воздерживайтесь от изменения встроенных прототипов во избежание непредвиденного поведения вашего кода или сторонних библиотек.
  • Используйте Object.defineProperty для безопасных расширений: когда нужно добавить методы, используйте Object.defineProperty, чтобы сделать их неперечисляемыми. Это предотвращает попадание ваших пользовательских свойств в циклы for...in и снижает вероятность конфликтов имён.
  • Используйте полифиллы осознанно: применяя полифиллы для добавления отсутствующих возможностей в старых браузерах, убедитесь, что они проверяют наличие метода перед тем, как добавить его в прототип.
  • Используйте современные возможности JavaScript: с развитием языка многие задачи, для которых раньше требовалось расширение встроенных прототипов, теперь решаются с помощью новых конструкций — классов и модулей.

Заключение

Встроенные прототипы — это механизм, лежащий в основе каждого встроенного метода, которым вы пользуетесь ежедневно: они располагаются в цепочке прототипов, с Object.prototype в корне, и позволяют таким значениям, как массивы, функции и числа, разделять поведение. Умение инспектировать их с помощью Object.getPrototypeOf, заимствовать их методы через call и apply, а также не поддаваться искушению расширять их глобально сделает ваш код более выразительным и предсказуемым.

Для более глубокого изучения смотрите прототипное наследование — там объясняется, как строится цепочка, — и методы прототипов и объекты без __proto__ — там описывается современный API Object.create / Object.getPrototypeOf.

Практика

Практика
Что верно в отношении расширения встроенных прототипов в JavaScript?
Что верно в отношении расширения встроенных прототипов в JavaScript?
Was this page helpful?