Прототипы — JS: Введение в ООП
В этом уроке дается небольшое введение в тему прототипов. Полностью раскрыть её одним уроком невозможно. Не хватит даже целого курса. Прототипы познаются только через практику, перемешанную с теоретической подготовкой. Поэтому не переживайте, если не всё поймёте с первого раза, это нормально.
Прототипы — это механизм, который оказывает основное влияние на то, как работают объекты в JavaScript. Сами они напрямую в коде используются редко (и обычно только в библиотеках), но их знание важно для понимания поведения кода и отладки. Особенно при работе с классами, которые мы изучим дальше по курсу. В этом уроке мы затронем только самые основы. Глубоко разобраться с прототипами поможет наш курс, указанный в дополнительных материалах.
В JavaScript с каждым объектом связан прототип. Прототип – это обычный объект, хранящийся в специальном служебном поле [[prototype]] (к этому полю невозможно обратиться напрямую). Его можно извлечь так:
const date = new Date(); // Эта функция извлекает прототип объекта из самого объекта const proto = Object.getPrototypeOf(date); // Date <> // В прототипе хранится не конструктор // Что там хранится – узнаем дальше proto === Date; // false const numbers = [1, 2]; Object.getPrototypeOf(numbers); // [] – отображение отличается, но это массив // Прототипы есть и у конструкторов, которые мы определяем сами function Company(name) this.name = name; > const company = new Company(); Object.getPrototypeOf(company); // Company <>
Для чего нужны прототипы
Представим, что мы обращаемся к свойству объекта, и при этом никакого свойства в этом объекте нет. В таком случае мы получим значение undefined . Обычно это так и работает, но есть одна важная особенность.
Если свойства в объекте нет, то JavaScript смотрит прототип этого объекта. Если в прототипе есть искомое свойство, то его значение возвращается. В итоге мы можем обратиться к свойству, которого нет в объекте, но есть в прототипе. И тогда мы получим из прототипа какое-то значение.
В реальности процесс еще сложнее. Если свойство не найдено в прототипе, то JavaScript смотрит прототип прототипа и так далее. Так он проходит до конца цепочки прототипов, то есть до последнего прототипа — это всегда null . На базе этого механизма реализуется наследование. Эта тема выходит за рамки текущего урока.
Прототипы есть даже у обычных JavaScript-объектов:
Object.getPrototypeOf(<>); // <> — это и есть Object
Именно по этой причине даже пустые объекты содержат свойства и методы:
const obj = <>; // То же самое можно сделать так: const obj = new Object(); // Это функция-конструктор, из которой был получен текущий объект, в нашем случае — Object obj.constructor; // [Function: Object] // У obj нет своего собственного свойства constructor, оно пришло из прототипа Object.hasOwn(obj, 'constructor'); // false Object.hasOwn(obj, 'name'); // false obj.name = 'hexlet'; // Имя есть в самом объекте, потому что мы его только что добавили Object.hasOwn(obj, 'name'); // true
Доступ к прототипу можно получить не только из объектов, но и из свойства prototype конструктора, который эти объекты создаёт:
function Company(name) this.name = name; > // Одно и то же, полученное разными способами // Company.prototype === Object.getPrototypeOf(new Company())
Теперь мы можем ответить на вопрос, откуда берется прототип. Прототип – это объект, находящийся в свойстве prototype функции-конструктора, а не сам конструктор. Проверить работу прототипов достаточно легко, изменив их:
// Добавляем свойство getName (делаем его методом) Company.prototype.getName = function getName() // this по-прежнему зависит от контекста, в котором вызывается return this.name; > const company = new Company('Hexlet'); // Свойство доступно! console.log(company.getName()); // => Hexlet
При этом никто не мешает заменить значение свойства getName в конкретном объекте. Это никаким образом не отразится на других объектах, так как они извлекают getName из прототипа:
const company1 = new Company('Hexlet'); const company2 = new Company('Google'); company2.getName = function getName() return 'Alphabet'; > // Этот вызов возьмет свойство из самого объекта company2.getName(); // Alphabet // Этот вызов возьмет значение свойства из прототипа company1.getName(); // Hexlet
Создание свойств через прототип – и есть правильный способ создания своих абстракций в JavaScript. Любая новая абстракция, которая нам нужна в коде, должна выглядеть как конструктор и прототип, наполненный нужными свойствами.
Что даёт этот механизм?
Самое простое – расширение ядра языка и библиотек без прямого доступа к исходному коду. Прототипы – невероятно гибкий механизм, который позволяет менять всё что угодно из любого места программы в рантайме, то есть во время работы. Например, мы можем добавить или заменить любые методы в любых объектах самого языка:
const numbers1 = [1, 3]; // Как только выполнится этот код, все массивы, // включая уже созданные, обзаведутся методом last Array.prototype.last = function last() // Такое обращение сработает, ведь this — это ссылка на сам объект, // который в нашем случае является массивом return this[this.length - 1]; > numbers1.last(); // 3 const numbers2 = [10, 0, -2]; numbers2.last(); // -2 // Пример замены // Это запрещенный прием, никогда так не делайте в реальном коде Array.prototype.map = function map() return 'Ehu!'; > numbers1.map(); // "Ehu!"
Как и любой другой мощный механизм, прототипы опасны. Можно наменять такого, что программирование превратится в ад:
// Очень злой код, который ломает работу метода push Array.prototype.push = function push(value) return this.unshift(value); > const numbers = [1, 2]; numbers.push(3); console.log(numbers); // => [3, 1, 2] .
Открыть доступ
Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно
- 130 курсов, 2000+ часов теории
- 1000 практических заданий в браузере
- 360 000 студентов
Наши выпускники работают в компаниях:
Прототип объекта
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/prototype-inheritance.
Объекты в JavaScript можно организовать в цепочки так, чтобы свойство, не найденное в одном объекте, автоматически искалось бы в другом.
Связующим звеном выступает специальное свойство __proto__ .
Прототип proto
Если один объект имеет специальную ссылку __proto__ на другой объект, то при чтении свойства из него, если свойство отсутствует в самом объекте, оно ищется в объекте __proto__ .
Свойство __proto__ доступно во всех браузерах, кроме IE10-, а в более старых IE оно, конечно же, тоже есть, но напрямую к нему не обратиться, требуются чуть более сложные способы, которые мы рассмотрим позднее.
Пример кода (кроме IE10-):
var animal = < eats: true >; var rabbit = < jumps: true >; rabbit.__proto__ = animal; // в rabbit можно найти оба свойства alert( rabbit.jumps ); // true alert( rabbit.eats ); // true
- Первый alert здесь работает очевидным образом – он выводит свойство jumps объекта rabbit .
- Второй alert хочет вывести rabbit.eats , ищет его в самом объекте rabbit , не находит – и продолжает поиск в объекте rabbit.__proto__ , то есть, в данном случае, в animal .
Иллюстрация происходящего при чтении rabbit.eats (поиск идёт снизу вверх):

Объект, на который указывает ссылка __proto__ , называется «прототипом». В данном случае получилось, что animal является прототипом для rabbit .
Также говорят, что объект rabbit «прототипно наследует» от animal .
Обратим внимание – прототип используется исключительно при чтении. Запись значения, например, rabbit.eats = value или удаление delete rabbit.eats – работает напрямую с объектом.
В примере ниже мы записываем свойство в сам rabbit , после чего alert перестаёт брать его у прототипа, а берёт уже из самого объекта:
var animal = < eats: true >; var rabbit = < jumps: true, eats: false >; rabbit.__proto__ = animal; alert( rabbit.eats ); // false, свойство взято из rabbit
Другими словами, прототип – это «резервное хранилище свойств и методов» объекта, автоматически используемое при поиске.
У объекта, который является __proto__ , может быть свой __proto__ , у того – свой, и так далее. При этом свойства будут искаться по цепочке.
Ссылка proto в спецификации
Если вы будете читать спецификацию ECMAScript – свойство __proto__ обозначено в ней как [[Prototype]] .
Двойные квадратные скобки здесь важны, чтобы не перепутать его с совсем другим свойством, которое называется prototype , и которое мы рассмотрим позже.
Метод hasOwnProperty
Обычный цикл for..in не делает различия между свойствами объекта и его прототипа.
Он перебирает всё, например:
var animal = < eats: true >; var rabbit = < jumps: true, __proto__: animal >; for (var key in rabbit) < alert( key + " = " + rabbit[key] ); // выводит и "eats" и "jumps" >
Иногда хочется посмотреть, что находится именно в самом объекте, а не в прототипе.
Вызов obj.hasOwnProperty(prop) возвращает true , если свойство prop принадлежит самому объекту obj , иначе false .
var animal = < eats: true >; var rabbit = < jumps: true, __proto__: animal >; alert( rabbit.hasOwnProperty('jumps') ); // true: jumps принадлежит rabbit alert( rabbit.hasOwnProperty('eats') ); // false: eats не принадлежит
Для того, чтобы перебрать свойства самого объекта, достаточно профильтровать key через hasOwnProperty :
var animal = < eats: true >; var rabbit = < jumps: true, __proto__: animal >; for (var key in rabbit) < if (!rabbit.hasOwnProperty(key)) continue; // пропустить "не свои" свойства alert( key + " = " + rabbit[key] ); // выводит только "jumps" >
Object.create(null)
Зачастую объекты используют для хранения произвольных значений по ключу, как коллекцию:
var data = <>; data.text = "Привет"; data.age = 35; // .
При дальнейшем поиске в этой коллекции мы найдём не только text и age , но и встроенные функции:
var data = <>; alert(data.toString); // функция, хотя мы её туда не записывали
Это может быть неприятным сюрпризом и приводить к ошибкам, если названия свойств приходят от посетителя и могут быть произвольными.
Чтобы этого избежать, мы можем исключать свойства, не принадлежащие самому объекту:
var data = <>; // выведет toString только если оно записано в сам объект alert(data.hasOwnProperty('toString') ? data.toString : undefined);
Однако, есть путь и проще:
var data = Object.create(null); data.text = "Привет"; alert(data.text); // Привет alert(data.toString); // undefined
Объект, создаваемый при помощи Object.create(null) не имеет прототипа, а значит в нём нет лишних свойств. Для коллекции – как раз то, что надо.
Методы для работы с proto
В современных браузерах есть два дополнительных метода для работы с __proto__ . Зачем они нужны, если есть __proto__ ? В общем-то, не очень нужны, но по историческим причинам тоже существуют.
Чтение: Object.getPrototypeOf(obj) Возвращает obj.__proto__ (кроме IE8-) Запись: Object.setPrototypeOf(obj, proto) Устанавливает obj.__proto__ = proto (кроме IE10-).
Кроме того, есть ещё один вспомогательный метод:
Создание объекта с прототипом: Object.create(proto, descriptors) Создаёт пустой объект с __proto__ , равным первому аргументу (кроме IE8-), второй необязательный аргумент может содержать дескрипторы свойств.
Итого
- В JavaScript есть встроенное «наследование» между объектами при помощи специального свойства __proto__ .
- При установке свойства rabbit.__proto__ = animal говорят, что объект animal будет «прототипом» rabbit .
- При чтении свойства из объекта, если его в нём нет, оно ищется в __proto__ . Прототип задействуется только при чтении свойства. Операции присвоения obj.prop = или удаления delete obj.prop совершаются всегда над самим объектом obj .
Несколько прототипов одному объекту присвоить нельзя, но можно организовать объекты в цепочку, когда один объект ссылается на другой при помощи __proto__ , тот ссылается на третий, и так далее.
В современных браузерах есть методы для работы с прототипом:
- Object.getPrototypeOf(obj) (кроме IE8-)
- Object.setPrototypeOf(obj, proto) (кроме IE10-)
- Object.create(proto, descriptors) (кроме IE8-)
Возможно, вас смущает недостаточная поддержка __proto__ в старых IE. Но это не страшно. В последующих главах мы рассмотрим дополнительные методы работы с __proto__ , включая те, которые работают везде.
Также мы рассмотрим, как свойство __proto__ используется внутри самого языка JavaScript и как организовать классы с его помощью.
Прототипы объектов
JavaScript часто описывают как язык прототипного наследования — каждый объект, имеет объект-прототип, который выступает как шаблон, от которого объект наследует методы и свойства. Объект-прототип так же может иметь свой прототип и наследовать его свойства и методы и так далее. Это часто называется цепочкой прототипов и объясняет почему одним объектам доступны свойства и методы которые определены в других объектах.
Точнее, свойства и методы определяются в свойстве prototype функции-конструктора объектов, а не в самих объектах.
В JavaScript создаётся связь между экземпляром объекта и его прототипом (свойство __proto__ , которое является производным от свойства prototype конструктора), а свойства и методы обнаруживаются при переходе по цепочке прототипов.
Примечание: Важно понимать, что существует различие между прототипом объекта (который доступен через Object.getPrototypeOf(obj) или через устаревшее свойство __proto__ ) и свойством prototype в функциях-конструкторах. Первое свойство является свойством каждого экземпляра, а второе — свойством конструктора. То есть Object.getPrototypeOf(new Foobar()) относится к тому же объекту, что и Foobar.prototype .
Давайте посмотрим на пример, чтобы стало понятнее.
Понимание прототипа объектов
Вернёмся к примеру, когда мы закончили писать наш конструктор Person() — загрузите пример в свой браузер. Если у вас ещё нет работы от последней статьи, используйте наш пример oojs-class-further-exercises.html (см. Также исходный код).
В этом примере мы определили конструктору функцию, например:
function Person(first, last, age, gender, interests) // Определения методов и свойств this.name = first: first, last: last, >; this.age = age; this.gender = gender; //. см. Введение в объекты для полного определения >
Затем мы создаём экземпляр объекта следующим образом:
var person1 = new Person("Bob", "Smith", 32, "male", ["music", "skiing"]);
Если вы наберёте « person1. » в вашей консоли JavaScript, вы должны увидеть, что браузер пытается автоматически заполнить это с именами участников, доступных на этом объекте:

В этом списке вы увидите элементы, определённые в конструкторе person 1 — Person() — name , age , gender , interests , bio , и greeting . Однако вы также увидите некоторые другие элементы — watch , valueOf и т. д. — они определены в объекте прототипа Person (), который является Object .

Итак, что произойдёт, если вы вызываете метод в person1 , который фактически определён в Object ? Например:
.valueOf();
Этот метод — Object.valueOf() наследуется person1 , потому что его конструктором является Person() , а прототипом Person() является Object() . valueOf() возвращает значение вызываемого объекта — попробуйте и убедитесь! В этом случае происходит следующее:
- Сначала браузер проверяет, имеет ли объект person1 доступный в нем метод valueOf() , как определено в его конструкторе Person() .
- Это не так, поэтому следующим шагом браузер проверяет, имеет ли прототип объекта ( Object() ) конструктора Person() доступный в нем метод valueOf() . Так оно и есть, поэтому он вызывается, и все хорошо!
Примечание: Мы хотим повторить, что методы и свойства не копируются из одного объекта в другой в цепочке прототипов — к ним обращаются, поднимаясь по цепочке, как описано выше.
Примечание: Официально нет способа получить доступ к объекту прототипа объекта напрямую — «ссылки» между элементами в цепочке определены во внутреннем свойстве, называемом [[prototype]] в спецификации для языка JavaScript ( см. ECMAScript). Однако у большинства современных браузеров есть свойство, доступное для них под названием __proto__ (это 2 подчёркивания с обеих сторон), который содержит объект-прототип объекта-конструктора. Например, попробуйте person1.__proto__ и person1.__proto__.__proto__ , чтобы увидеть, как выглядит цепочка в коде!
С ECMAScript 2015 вы можете косвенно обращаться к объекту прототипа объекта Object.getPrototypeOf (obj) .
Свойство prototype: Где определены унаследованные экземпляры
Итак, где определены наследуемые свойства и методы? Если вы посмотрите на страницу со ссылкой Object , вы увидите в левой части большое количество свойств и методов — это намного больше, чем количество унаследованных членов, доступных для объекта person1 . Некоторые из них унаследованы, а некоторые нет — почему это?
Как упоминалось выше, наследованные свойства это те, что определены в свойстве prototype (вы можете называть это подпространством имён), то есть те, которые начинаются с Object.prototype. , а не те, которые начинаются с простого Object . Значение свойства prototype — это объект, который в основном представляет собой контейнер для хранения свойств и методов, которые мы хотим наследовать объектами, расположенными дальше по цепочке прототипов.
Таким образом Object.prototype.watch() , Object.prototype.valueOf() и т. д. доступны для любых типов объектов, которые наследуются от Object.prototype , включая новые экземпляры объектов, созданные из конструктора Person() .
Object.is() , Object.keys() и другие члены, не определённые в контейнере prototype , не наследуются экземплярами объектов или типами объектов, которые наследуются от Object.prototype . Это методы / свойства, доступные только в конструкторе Object() .
Примечание: Это кажется странным — как у вас есть метод, определённый для конструктора, который сам по себе является функцией? Ну, функция также является типом объекта — см. Ссылку на конструктор Function() , если вы нам не верите.
- Вы можете проверить существующие свойства прототипа для себя — вернитесь к нашему предыдущему примеру и попробуйте ввести следующее в консоль JavaScript:
Person.prototype;
Object.prototype;
Вы увидите большое количество методов, определённых для свойства prototype Object ‘а , которые затем доступны для объектов, которые наследуются от Object , как показано выше.
Вы увидите другие примеры наследования цепочек прототипов по всему JavaScript — попробуйте найти методы и свойства, определённые на прототипе глобальных объектов String , Date , Number и Array , например. Все они имеют несколько элементов, определённых на их прототипе, поэтому, например, когда вы создаёте строку, вот так:
var myString = "This is my string.";
В myString сразу есть множество полезных методов, таких как split() , indexOf() , replace() и т. д.
Предупреждение: Важно: Свойство prototype является одной из наиболее противоречивых названий частей JavaScript — вы можете подумать, что this указывает на объект прототипа текущего объекта, но это не так (это внутренний объект, к которому можно получить доступ __proto__ , помните ?). prototype вместо этого — свойство, содержащее объект, на котором вы определяете членов, которые вы хотите наследовать.
Снова create()
Ранее мы показали, как метод Object.create() может использоваться для создания нового экземпляра объекта.
-
Например, попробуйте это в консоли JavaScript предыдущего примера:
var person2 = Object.create(person1);
.__proto__;
Это вернёт объект person1.
Свойство constructor
Каждая функция-конструктор имеет свойство prototype , значением которого является объект, содержащий свойство constructor . Это свойство constructor указывает на исходную функцию-конструктор. Как вы увидите в следующем разделе, свойства, определённые в свойстве Person.prototype (или в общем случае в качестве свойства прототипа функции конструктора, который является объектом, как указано в предыдущем разделе) становятся доступными для всех объектов экземпляра, созданных с помощью конструктор Person() . Следовательно, свойство конструктора также доступно для объектов person1 и person2 .
-
Например, попробуйте эти команды в консоли:
.constructor; person2.constructor;
var person3 = new person1.constructor("Karen", "Stephenson", 26, "female", [ "playing drums", "mountain climbing", ]);
.name.first; person3.age; person3.bio();
Это хорошо работает. Вам не нужно будет использовать его часто, но это может быть действительно полезно, если вы хотите создать новый экземпляр и не имеете ссылки на исходный конструктор, который легко доступен по какой-либо причине.
Свойство constructor имеет другие применения. Например, если у вас есть экземпляр объекта и вы хотите вернуть имя конструктора этого экземпляра, вы можете использовать следующее:
.constructor.name;
Например, попробуйте это:
.constructor.name;
Примечание: Значение constructor.name может измениться (из-за прототипического наследования, привязки, препроцессоров, транспилеров и т. д.), Поэтому для более сложных примеров вы захотите использовать оператор instanceof .
Изменение прототипов
Давайте рассмотрим пример изменения свойства prototype функции-конструктора — методы, добавленные в прототип, затем доступны для всех экземпляров объектов, созданных из конструктора.
-
Вернитесь к нашему примеру oojs-class-further-exercises.html и создайте локальную копию исходного кода. Ниже существующего JavaScript добавьте следующий код, который добавляет новый метод в свойство prototype конструктора:
Person.prototype.farewell = function () alert(this.name.first + " has left the building. Bye for now!"); >;
.farewell();
Должно появиться всплывающее окно, с именем пользователя, определённым в конструкторе. Это действительно полезно, но ещё более полезно то, что вся цепочка наследования обновляется динамически, автоматически делая этот новый метод доступным для всех экземпляров объектов, полученных из конструктора.
Подумайте об этом на мгновение. В нашем коде мы определяем конструктор, затем мы создаём экземпляр объекта из конструктора, затем добавляем новый метод к прототипу конструктора:
function Person(first, last, age, gender, interests) // определения свойств и методов > var person1 = new Person("Tammi", "Smith", 32, "neutral", [ "music", "skiing", "kickboxing", ]); Person.prototype.farewell = function () alert(this.name.first + " has left the building. Bye for now!"); >;
Но метод farewell() по-прежнему доступен в экземпляре объекта person1 — его элементы были автоматически обновлены, чтобы включить недавно определённый метод farewell() .
Примечание: Если у вас возникли проблемы с получением этого примера для работы, посмотрите на наш пример oojs-class-prototype.html (см. также это running live).
Вы редко увидите свойства, определённые в свойстве prototype , потому что они не очень гибки при таком определении. Например, вы можете добавить свойство следующим образом:
Person.prototype.fullName = "Bob Smith";
Это не очень гибко, так как человека нельзя назвать так. Было бы намного лучше сделать это, создав fullName из name.first и name.last :
Person.prototype.fullName = this.name.first + " " + this.name.last;
Однако это не работает, поскольку в этом случае this будет ссылаться на глобальную область, а не на область функции. Вызов этого свойства вернёт undefined undefined . Это отлично работало с методом, который мы определили ранее в прототипе, потому что он находится внутри области функций, которая будет успешно перенесена в область экземпляра объекта. Таким образом, вы можете определить постоянные свойства прототипа (т. е. те, которые никогда не нуждаются в изменении), но обычно лучше определять свойства внутри конструктора.
Фактически, довольно распространённый шаблон для большего количества определений объектов — это определение свойств внутри конструктора и методов в прототипе. Это упрощает чтение кода, поскольку конструктор содержит только определения свойств, а методы разделены на отдельные блоки. Например:
// Определение конструктора и его свойств function Test(a, b, c, d) // определение свойств. > // Определение первого метода Test.prototype.x = function() . >; // Определение второго метода Test.prototype.y = function() . >; //. и так далее
Этот образец можно увидеть в действии в примере приложения плана школы Петра Залевы.
Резюме
В этой статье рассмотрены прототипы объектов JavaScript (в том числе и то, как прототип цепочки объектов позволяет объектам наследовать функции друг от друга), свойство прототипа и как его можно использовать для добавления методов к конструкторам и другие связанные с этой статьёй темы.
В следующей статье мы рассмотрим то, как вы можете реализовать наследование функциональности между двумя собственными настраиваемыми объектами.
В этом модуле
- Основы объекта
- Объектно-ориентированный JavaScript для начинающих
- Прототипы объектов
- Наследование в JavaScript
- Работа с данными JSON
- Практика построения объектов
- Добавление функций в нашу демонстрацию прыгающих шаров
Found a content problem with this page?
- Edit the page on GitHub.
- Report the content issue.
- View the source on GitHub.
This page was last modified on 3 авг. 2023 г. by MDN contributors.
Your blueprint for a better internet.
Объектно-ориентированный JavaScript
Хотя JavaScript не является языком, основанным на классах, и не реализует объектно-ориентированное программирование в традиционном смысле, он предоставляет возможности и шаблоны, которые позволяют использовать концепции объектно-ориентированного программирования. Это можно назвать прототипным наследованием.
Что такое прототип в JavaScript?
Основная концепция прототипа в JavaScript заключается в том, что один объект может наследовать свойства и методы от другого. На приведенном ниже генеалогическом дереве видно, что люди в нижней части связаны с одним человеком на вершине, и путь снизу вверх можно проследить шаг за шагом.
В JavaScript прототип можно рассматривать как проект, шаблон или базовый объект, содержащий методы, к которым может обратиться любой созданный экземпляр объекта. Если бы не было прототипа, то пришлось бы определять методы для каждого отдельного экземпляра объекта. Этот процесс неэффективен и занимает много памяти.
Разберем два прототипа: свойство prototype и сам прототип объекта. Начнем со свойства prototype .
Свойство prototype
Когда мы используем методы JavaScript, такие как pop в отношении массива или include при работе со строками, мы применяем встроенные методы JavaScript. Рассмотрим это на примере. Начнем с простого доступа к свойству prototype в конструкторе прототипов Array .
Теперь создадим массив и посмотрим, как можно просматривать свойство prototype внутри массива.
В приведенном выше примере переменная ourArray объявляется и инициализируется массивом, содержащим значения 1, 2 и 3. Позже нужно будет рассмотреть массив ourArray . Чтобы удалить элемент из конца массива, можно использовать метод pop . Но откуда он берется? Взглянем на массив ourArray еще раз.
Каждый объект в JavaScript имеет свойство [[Prototype]] . Это внутреннее свойство, которое обозначается двойными скобками, в которые оно заключено. Выбрав свойство [[Prototype]] , мы видим доступные методы, которые в данном примере наследуются от конструктора Array . Мы также можем использовать метод getPrototypeOf , который вернет свойство [[Prototype]] данного объекта (помните, что массивы — это особый тип объектов).
Вы также можете встретить примеры, в которых используется свойство __proto__ . Это унаследованная функция, и в eslint есть правило (no-proto), защищающее от ее использования. Свойство __proto__ раскрывает внутренний [[Prototype]] объекта. Лучше использовать getPrototypeOf , как показано ниже.
Мы можем провести сравнение с методами, перечисленными на сайте mdn. Итак, все массивы могут получить доступ к этим методам, но сами они не обладают ими в качестве свойств.
Эти методы может использовать каждый массив. Но вместо отдельного определения в каждом массиве методы определяются в объекте-прототипе. Это относится не только к массивам, но и к другим встроенным объектам в JavaScript. Эту особенность можно применять и при создании объектов.
Как это возможно?
Объект, который является значением свойства [[prototype]] , — это прототип для того объекта, который мы рассматриваем. Если он не существует, его значение будет равно null . Когда мы используем метод pop на ourArray , интерпретатор JavaScript сначала будет искать этот метод в ourArray . Если он не найдет его там, то будет искать в прототипе.
Этот процесс называется наследованием прототипа или цепочкой прототипов. Его можно продолжать до бесконечности, но лучше не создавать сложную цепочку, потому что отладка может привести к путанице. Использование цепочки прототипов помогает хранить функцию только в одном объекте, и интерпретатор будет искать ее именно в нем, если не найдет в первом объекте. Возьмем для примера код.
function GameTracker(name, result) this.name = name;
this.result = result;
>
В приведенном выше коде мы объявляем функцию-конструктор под названием GameTracker с параметрами name и result . Если эта функция хранится в памяти, она хранится как функция с объектом.
У объекта есть свойство prototype , которое представляет собой пустой объект. Любые функции, которые создаются с помощью конструктора GameTracker , будут иметь доступ к свойству prototype . С его помощью внутри функции мы устанавливаем параметры name и result , чтобы они ссылались на текущий экземпляр объекта.
let playerOne = new GameTracker("Fred", 8);
Далее мы создаем переменную playerOne , изначально неинициализированную. Затем с помощью ключевого слова new создается новый экземпляр GameTracker . Когда мы используем ключевое слово new , устанавливается свойство prototype , которое будет ссылкой на объект GameTracker . Если мы рассмотрим переменную playerOne , то увидим следующее:
console.log(playerOne);
//Возвращает ---> GameTracker
Теперь попробуем определиться с конструктором playerOne :
Object.getPrototypeOf(playerOne);//Возвращает ---> constructor: ƒ GameTracker(name, result)
Используя прототип, можно добавить метод к прототипу. Добавим метод для запуска игры:
GameTracker.prototype.start = function() return `Hello $ the game is ready.`;
>
Теперь снова посмотрим на объект playerOne . Мы видим, что функция start хранится в прототипе.
Мы можем использовать функцию start для playerOne . Попробуем это сделать:
playerOne.start();//Возвращает ---> 'Hello Fred the game is ready.'
- Паттерн “Шаблонный метод” и его реализация в JavaScript
- Как обучать программированию подростков?
- JavaScript async/await: что хорошего, в чём опасность и как применять?