- Встроенные прототипы
- Object.prototype
- Примитивы
- Изменение встроенных прототипов
- Заимствование у прототипов
- Итого
- Задачи
- Добавить функциям метод «f.defer(ms)»
- JavaScript Object Prototypes
- Example
- Example
- Example
- Prototype Inheritance
- Adding Properties and Methods to Objects
- Using the prototype Property
- Example
- Example
Встроенные прототипы
Свойство «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 уже наследует от другого объекта. Помните, мы можем наследовать только от одного объекта одновременно.
Заимствование методов – гибкий способ, позволяющий смешивать функциональность разных объектов по необходимости.
Итого
- Все встроенные объекты следуют одному шаблону:
- Методы хранятся в прототипах ( Array.prototype , Object.prototype , Date.prototype и т.д.).
- Сами объекты хранят только данные (элементы массивов, свойства объектов, даты).
Задачи
Добавить функциям метод «f.defer(ms)»
Добавьте всем функциям в прототип метод defer(ms) , который вызывает функции через ms миллисекунд.
После этого должен работать такой код:
function f() < alert("Hello!"); >f.defer(1000); // выведет "Hello!" через 1 секунду
JavaScript Object Prototypes
All JavaScript objects inherit properties and methods from a prototype.
In the previous chapter we learned how to use an object constructor:
Example
function Person(first, last, age, eyecolor) <
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
>const myFather = new Person(«John», «Doe», 50, «blue»);
const myMother = new Person(«Sally», «Rally», 48, «green»);We also learned that you can not add a new property to an existing object constructor:
Example
To add a new property to a constructor, you must add it to the constructor function:
Example
function Person(first, last, age, eyecolor) <
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
this.nationality = «English»;
>Prototype Inheritance
All JavaScript objects inherit properties and methods from a prototype:
- Date objects inherit from Date.prototype
- Array objects inherit from Array.prototype
- Person objects inherit from Person.prototype
The Object.prototype is on the top of the prototype inheritance chain:
Date objects, Array objects, and Person objects inherit from Object.prototype .
Adding Properties and Methods to Objects
Sometimes you want to add new properties (or methods) to all existing objects of a given type.
Sometimes you want to add new properties (or methods) to an object constructor.
Using the prototype Property
The JavaScript prototype property allows you to add new properties to object constructors:
Example
function Person(first, last, age, eyecolor) <
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
>The JavaScript prototype property also allows you to add new methods to objects constructors:
Example
function Person(first, last, age, eyecolor) <
this.firstName = first;
this.lastName = last;
this.age = age;
this.eyeColor = eyecolor;
>Person.prototype.name = function() return this.firstName + » » + this.lastName;
>;Only modify your own prototypes. Never modify the prototypes of standard JavaScript objects.