Javascript create new prototype

Свойство F.prototype и создание объектов через new

Материал на этой странице устарел, поэтому скрыт из оглавления сайта.

Более новая информация по этой теме находится на странице https://learn.javascript.ru/function-prototype.

До этого момента мы говорили о наследовании объектов, объявленных через <. >.

Но в реальных проектах объекты обычно создаются функцией-конструктором через new . Посмотрим, как указать прототип в этом случае.

Свойство F.prototype

Самым очевидным решением является назначение __proto__ в конструкторе.

Например, если я хочу, чтобы у всех объектов, которые создаются new Rabbit , был прототип animal , я могу сделать так:

var animal = < eats: true >; function Rabbit(name) < this.name = name; this.__proto__ = animal; >var rabbit = new Rabbit("Кроль"); alert( rabbit.eats ); // true, из прототипа

Недостаток этого подхода – он не работает в IE10-.

К счастью, в JavaScript с древнейших времён существует альтернативный, встроенный в язык и полностью кросс-браузерный способ.

Чтобы новым объектам автоматически ставить прототип, конструктору ставится свойство prototype .

При создании объекта через new , в его прототип __proto__ записывается ссылка из prototype функции-конструктора.

Например, код ниже полностью аналогичен предыдущему, но работает всегда и везде:

var animal = < eats: true >; function Rabbit(name) < this.name = name; >Rabbit.prototype = animal; var rabbit = new Rabbit("Кроль"); // rabbit.__proto__ == animal alert( rabbit.eats ); // true

Установка Rabbit.prototype = animal буквально говорит интерпретатору следующее: «При создании объекта через new Rabbit запиши ему __proto__ = animal «.

Свойство с именем prototype можно указать на любом объекте, но особый смысл оно имеет, лишь если назначено функции-конструктору.

Само по себе, без вызова оператора new , оно вообще ничего не делает, его единственное назначение – указывать __proto__ для новых объектов.

Технически, в это свойство можно записать что угодно.

Однако, при работе new , свойство prototype будет использовано лишь в том случае, если это объект. Примитивное значение, такое как число или строка, будет проигнорировано.

Свойство constructor

У каждой функции по умолчанию уже есть свойство prototype .

Оно содержит объект такого вида:

function Rabbit() <> Rabbit.prototype = < constructor: Rabbit >;

В коде выше я создал Rabbit.prototype вручную, но ровно такой же – генерируется автоматически.

function Rabbit() <> // в Rabbit.prototype есть одно свойство: constructor alert( Object.getOwnPropertyNames(Rabbit.prototype) ); // constructor // оно равно Rabbit alert( Rabbit.prototype.constructor == Rabbit ); // true

Можно его использовать для создания объекта с тем же конструктором, что и данный:

function Rabbit(name) < this.name = name; alert( name ); >var rabbit = new Rabbit("Кроль"); var rabbit2 = new rabbit.constructor("Крольчиха");

Эта возможность бывает полезна, когда, получив объект, мы не знаем в точности, какой у него был конструктор (например, сделан вне нашего кода), а нужно создать такой же.

JavaScript никак не использует свойство constructor . То есть, оно создаётся автоматически, а что с ним происходит дальше – это уже наша забота. В стандарте прописано только его создание.

В частности, при перезаписи Rabbit.prototype = < jumps: true >свойства constructor больше не будет.

Сам интерпретатор JavaScript его в служебных целях не требует, поэтому в работе объектов ничего не «сломается». Но если мы хотим, чтобы возможность получить конструктор, всё же, была, то можно при перезаписи гарантировать наличие constructor вручную:

Либо можно поступить аккуратно и добавить свойства к встроенному prototype без его замены:

// сохранится встроенный constructor Rabbit.prototype.jumps = true

Эмуляция Object.create для IE8-

Как мы только что видели, с конструкторами всё просто, назначить прототип можно кросс-браузерно при помощи F.prototype .

Теперь небольшое «лирическое отступление» в область совместимости.

Прямые методы работы с прототипом отсутствуют в старых IE, но один из них – Object.create(proto) можно эмулировать, как раз при помощи prototype . И он будет работать везде, даже в самых устаревших браузерах.

Кросс-браузерный аналог – назовём его inherit , состоит буквально из нескольких строк:

function inherit(proto) < function F() <>F.prototype = proto; var object = new F; return object; >

Результат вызова inherit(animal) идентичен Object.create(animal) . Она создаёт новый пустой объект с прототипом animal .

var animal = < eats: true >; var rabbit = inherit(animal); alert( rabbit.eats ); // true

Посмотрите внимательно на функцию inherit и вы, наверняка, сами поймёте, как она работает…

Если где-то неясности, то её построчное описание:

function inherit(proto) < function F() <>// (1) F.prototype = proto // (2) var object = new F; // (3) return object; // (4) >
  1. Создана новая функция F . Она ничего не делает с this , так что если вызвать new F , то получим пустой объект.
  2. Свойство F.prototype устанавливается в будущий прототип proto
  3. Результатом вызова new F будет пустой объект с __proto__ равным значению F.prototype .
  4. Мы получили пустой объект с заданным прототипом, как и хотели. Возвратим его.

Для унификации можно запустить такой код, и метод Object.create станет кросс-браузерным:

if (!Object.create) Object.create = inherit; /* определение inherit - выше */

В частности, аналогичным образом работает библиотека es5-shim, при подключении которой Object.create станет доступен для всех браузеров.

Итого

Для произвольной функции – назовём её Person , верно следующее:

  • Прототип __proto__ новых объектов, создаваемых через new Person , можно задавать при помощи свойства Person.prototype .
  • Значением Person.prototype по умолчанию является объект с единственным свойством constructor , содержащим ссылку на Person . Его можно использовать, чтобы из самого объекта получить функцию, которая его создала. Однако, JavaScript никак не поддерживает корректность этого свойства, поэтому программист может его изменить или удалить.
  • Современный метод Object.create(proto) можно эмулировать при помощи prototype , если хочется, чтобы он работал в IE8-.

Задачи

Прототип после создания

В примерах ниже создаётся объект new Rabbit , а затем проводятся различные действия с prototype .

Каковы будут результаты выполнения? Почему?

Начнём с этого кода. Что он выведет?

function Rabbit() <> Rabbit.prototype = < eats: true >; var rabbit = new Rabbit(); alert( rabbit.eats );

Добавили строку (выделена), что будет теперь?

function Rabbit() <> Rabbit.prototype = < eats: true >; var rabbit = new Rabbit(); Rabbit.prototype = <>; alert( rabbit.eats );

А если код будет такой? (заменена одна строка):

function Rabbit(name) <> Rabbit.prototype = < eats: true >; var rabbit = new Rabbit(); Rabbit.prototype.eats = false; alert( rabbit.eats );

А такой? (заменена одна строка)

function Rabbit(name) <> Rabbit.prototype = < eats: true >; var rabbit = new Rabbit(); delete rabbit.eats; // (*) alert( rabbit.eats );
function Rabbit(name) <> Rabbit.prototype = < eats: true >; var rabbit = new Rabbit(); delete Rabbit.prototype.eats; // (*) alert( rabbit.eats );

Результат: true , из прототипа

Результат: true . Свойство prototype всего лишь задаёт __proto__ у новых объектов. Так что его изменение не повлияет на rabbit.__proto__ . Свойство eats будет получено из прототипа.

Результат: false . Свойство Rabbit.prototype и rabbit.__proto__ указывают на один и тот же объект. В данном случае изменения вносятся в сам объект.

Результат: true , так как delete rabbit.eats попытается удалить eats из rabbit , где его и так нет. А чтение в alert произойдёт из прототипа.

Результат: undefined . Удаление осуществляется из самого прототипа, поэтому свойство rabbit.eats больше взять неоткуда.

Аргументы по умолчанию

Есть функция Menu , которая получает аргументы в виде объекта options :

/* options содержит настройки меню: width, height и т.п. */ function Menu(options)

Ряд опций должны иметь значение по умолчанию. Мы могли бы проставить их напрямую в объекте options :

…Но такие изменения могут привести к непредвиденным результатам, т.к. объект options может быть повторно использован во внешнем коде. Он передаётся в Menu для того, чтобы параметры из него читали, а не писали.

Один из способов безопасно назначить значения по умолчанию – скопировать все свойства options в локальные переменные и затем уже менять. Другой способ – клонировать options путём копирования всех свойств из него в новый объект, который уже изменяется.

При помощи наследования и Object.create предложите третий способ, который позволяет избежать копирования объекта и не требует новых переменных.

Можно прототипно унаследовать от options и добавлять/менять опции в наследнике:

function Menu(options) < options = Object.create(options); options.width = 300; alert("width: " + options.width); // возьмёт width из наследника alert("height: " + options.height); // возьмёт height из исходного объекта >var options = < width: 100, height: 200 >; var menu = new Menu(options); alert("original width: " + options.width); // width исходного объекта alert("original height: " + options.height); // height исходного объекта

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

Комментарии

  • Если вам кажется, что в статье что-то не так — вместо комментария напишите на GitHub.
  • Для одной строки кода используйте тег , для нескольких строк кода — тег , если больше 10 строк — ссылку на песочницу (plnkr, JSBin, codepen…)
  • Если что-то непонятно в статье — пишите, что именно и с какого места.

Источник

Читайте также:  Python shutil copy all files
Оцените статью