Javascript objects with prototype

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

Свойство «prototype» широко используется внутри самого языка JavaScript. Все встроенные функции-конструкторы используют его.

Сначала мы рассмотрим детали, а затем используем «prototype» для добавления встроенным объектам новой функциональности.

Object.prototype

Давайте выведем пустой объект:

let obj = <>; alert( obj ); // "[object Object]" ?

Где код, который генерирует строку «[object Object]» ? Это встроенный метод toString , но где он? obj ведь пуст!

…Но краткая нотация obj = <> – это то же самое, что и obj = new Object() , где Object – встроенная функция-конструктор для объектов с собственным свойством prototype , которое ссылается на огромный объект с методом toString и другими.

Другие встроенные объекты устроены аналогично. Даже функции – они объекты встроенного конструктора Function , и все их методы ( call / apply и другие) берутся из Function.prototype . Также у функций есть свой метод toString .

function f() <> alert(f.__proto__ == Function.prototype); // true alert(f.__proto__.__proto__ == Object.prototype); // true, наследует от Object

Примитивы

Самое сложное происходит со строками, числами и булевыми значениями.

Как мы помним, они не объекты. Но если мы попытаемся получить доступ к их свойствам, то тогда будет создан временный объект-обёртка с использованием встроенных конструкторов String , Number и Boolean , который предоставит методы и после этого исчезнет.

Эти объекты создаются невидимо для нас, и большая часть движков оптимизирует этот процесс, но спецификация описывает это именно таким образом. Методы этих объектов также находятся в прототипах, доступных как String.prototype , Number.prototype и Boolean.prototype .

Специальные значения null и undefined стоят особняком. У них нет объектов-обёрток, так что методы и свойства им недоступны. Также у них нет соответствующих прототипов.

Изменение встроенных прототипов

Встроенные прототипы можно изменять. Например, если добавить метод к String.prototype , метод становится доступен для всех строк:

String.prototype.show = function() < alert(this); >; "BOOM!".show(); // BOOM!

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

Прототипы глобальны, поэтому очень легко могут возникнуть конфликты. Если две библиотеки добавляют метод String.prototype.show , то одна из них перепишет метод другой.

Так что, в общем, изменение встроенных прототипов считается плохой идеей.

В современном программировании есть только один случай, в котором одобряется изменение встроенных прототипов. Это создание полифилов.

Полифил – это термин, который означает эмуляцию метода, который существует в спецификации JavaScript, но ещё не поддерживается текущим движком JavaScript.

Тогда мы можем реализовать его сами и добавить во встроенный прототип.

if (!String.prototype.repeat) < // Если такого метода нет // добавляем его в прототип String.prototype.repeat = function(n) < // повторить строку n раз // на самом деле код должен быть немного более сложным // (полный алгоритм можно найти в спецификации) // но даже неполный полифил зачастую достаточно хорош для использования return new Array(n + 1).join(this); >; > alert( "La".repeat(3) ); // LaLaLa

Заимствование у прототипов

В главе Декораторы и переадресация вызова, call/apply мы говорили о заимствовании методов.

Это когда мы берём метод из одного объекта и копируем его в другой.

Некоторые методы встроенных прототипов часто одалживают.

Например, если мы создаём объект, похожий на массив (псевдомассив), мы можем скопировать некоторые методы из Array в этот объект.

let obj = < 0: "Hello", 1: "world!", length: 2, >; obj.join = Array.prototype.join; alert( obj.join(',') ); // Hello,world!

Это работает, потому что для внутреннего алгоритма встроенного метода join важны только корректность индексов и свойство length , он не проверяет, является ли объект на самом деле массивом. И многие встроенные методы работают так же.

Альтернативная возможность – мы можем унаследовать от массива, установив obj.__proto__ как Array.prototype , таким образом все методы Array станут автоматически доступны в obj .

Но это будет невозможно, если obj уже наследует от другого объекта. Помните, мы можем наследовать только от одного объекта одновременно.

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

Итого

Оцените статью