Я не так увлекаюсь динамическими языками программирования, но я написал свою долю кода JavaScript. Я никогда не думал об этом программировании на основе прототипов, кто-нибудь знает, как это работает?
var obj = new Object();
obj.prototype.test = function() { alert('Hello?'); };
var obj2 = new obj();
obj2.test();
Я помню много дискуссий, которые у меня были с людьми некоторое время назад (я не совсем уверен, что я делаю), но, насколько я понимаю, понятия класса нет. Это просто объект, и экземпляры этих объектов являются клонами оригинала, верно?
Но какова точная цель этого свойства .prototype в JavaScript? Как это относится к созданию объектов?
Обновление: правильный путь
var obj = new Object(); // not a functional object
obj.prototype.test = function() { alert('Hello?'); }; // this is wrong!
function MyObject() {} // a first class functional object
MyObject.prototype.test = function() { alert('OK'); } // OK
Также эти слайды очень помогли.
javascript
dynamic-languages
prototype-oriented
Джон Лейдгрен
источник
источник
Ответы:
Каждый объект JavaScript имеет внутренний «слот», который называется
[[Prototype]]
либо,null
либо значениеobject
. Вы можете представить слот как свойство объекта, внутреннего для движка JavaScript, скрытого от кода, который вы пишете. Квадратные скобки вокруг[[Prototype]]
являются преднамеренными и являются соглашением спецификации ECMAScript для обозначения внутренних слотов.Значение, на которое указывает
[[Prototype]]
объект, в просторечии известно как «прототип этого объекта».Если вы обращаетесь к свойству через точку (
obj.propName
) или скобку (obj['propName']
), и у объекта нет такого свойства напрямую (т. Е. Собственное свойство , которое можно проверить с помощьюobj.hasOwnProperty('propName')
), среда выполнения ищет свойство с этим именем в объекте, на который ссылаются самого[[Prototype]]
вместо этого. Если свойство[[Prototype]]
также не имеет такого свойства,[[Prototype]]
оно проверяется по очереди и так далее. Таким образом, цепочка прототипов исходного объекта обходится, пока не будет найдено совпадение или пока не будет достигнут его конец. В верхней части цепочки прототипов находитсяnull
стоимость.Современные реализации JavaScript предоставляют доступ для чтения и / или записи
[[Prototype]]
в следующие способы:new
операторе (конфигурирует прототип цепь на объекте по умолчанию возвращается из функции конструктора),extends
Ключевое слово (настраивает цепочку прототипов при использовании синтаксиса класса),Object.create
установит предоставленный аргумент как[[Prototype]]
качестве результирующего объекта,Object.getPrototypeOf
иObject.setPrototypeOf
(получить / установить[[Prototype]]
после создания объекта), и__proto__
(аналогично 4.)Object.getPrototypeOf
иObject.setPrototypeOf
предпочтительнее__proto__
, отчасти потому , что поведениеo.__proto__
является необычным когда объект имеет прототипnull
.Объект
[[Prototype]]
изначально устанавливается при создании объекта.Если вы создаете новый объект через
new Func()
,[[Prototype]]
по умолчанию объект будет установлен на объект, на который ссылаетсяFunc.prototype
.Обратите внимание, что, следовательно, все классы и все функции, которые могут использоваться с
new
оператором, имеют свойство, названное.prototype
в дополнение к их собственному[[Prototype]]
внутреннему слоту. Это двойное использование слова «прототип» является источником бесконечной путаницы среди новичков в языке.Использование
new
функций конструктора позволяет нам моделировать классическое наследование в JavaScript; хотя система наследования JavaScript, как мы уже видели, прототипична, а не основана на классах.До введения синтаксиса классов в JavaScript функции конструктора были единственным способом имитации классов. Мы можем думать о свойствах объекта, на который ссылается
.prototype
свойство функции конструктора, как об общих членах; то есть. члены, которые являются одинаковыми для каждого экземпляра. В системах на основе классов методы реализуются одинаково для каждого экземпляра, поэтому методы концептуально добавляются в.prototype
свойство; поля объекта, однако, являются специфическими для экземпляра и поэтому добавляются к самому объекту во время построения.Без синтаксиса класса разработчикам пришлось вручную настраивать цепочку прототипов для достижения функциональности, аналогичной классическому наследованию. Это привело к преобладанию различных способов достижения этого.
Вот один из способов:
... и вот еще один способ:
Синтаксис класса, представленный в ES2015, упрощает работу, предоставляя
extends
«единственно верный способ» конфигурировать цепочку прототипов для имитации классического наследования в JavaScript.Итак, аналогично приведенному выше коду, если вы используете синтаксис класса для создания нового объекта следующим образом:
... результирующий объект
[[Prototype]]
будет установлен на экземплярParent
которого[[Prototype]]
, в свою очередь, являетсяParent.prototype
.Наконец, если вы создадите новый объект через
Object.create(foo)
, результирующий объект[[Prototype]]
будет установлен вfoo
.источник
В языке, реализующем классическое наследование, таком как Java, C # или C ++, вы начинаете с создания класса - проекта для ваших объектов - и затем вы можете создавать новые объекты из этого класса или расширять класс, определяя новый класс, который дополняет оригинальный класс.
В JavaScript вы сначала создаете объект (нет понятия класса), затем вы можете дополнить свой собственный объект или создать из него новые объекты. Это не сложно, но немного чуждо и трудно усваивается для того, кто привык к классическому образу жизни.
Пример:
До сих пор я расширял базовый объект, теперь я создаю другой объект, а затем наследую от Person.
Показать фрагмент кода
Хотя, как сказано, я не могу вызвать setAmountDue (), getAmountDue () для Person.
источник
Customer.prototype = new Person();
строки MDN показывает пример использованияCustomer.prototype = Object.create(Person.prototype)
и заявляет, что «Распространенной ошибкой здесь является использование« new Person () »» . источникЭто очень простая объектная модель на основе прототипа, которая будет рассмотрена в качестве примера при объяснении без комментариев:
Есть несколько важных моментов, которые мы должны рассмотреть, прежде чем перейти к концепции прототипа.
1- Как на самом деле работают функции JavaScript:
Чтобы сделать первый шаг, мы должны выяснить, как на самом деле работают функции JavaScript, как функции класса
this
ключевого слова в ней или просто как обычную функцию со своими аргументами, что она делает и что возвращает.Допустим, мы хотим создать
Person
объектную модель. но в этом шаге я буду пытаться сделать то же самое точное без использованияprototype
иnew
ключевых слов .Таким образом , в этом шаге
functions
,objects
иthis
ключевое слово, все у нас есть.Первый вопрос заключается в том, как
this
ключевое слово может быть полезным без использованияnew
ключевого слова. .Итак, чтобы ответить на это, скажем, у нас есть пустой объект и две функции, такие как:
и теперь без использования
new
ключевого слова как мы могли бы использовать эти функции. Таким образом, у JavaScript есть 3 различных способа сделать это:а. Первый способ - просто вызвать функцию как обычную функцию:
в этом случае это будет текущий объект контекста, который обычно является глобальным
window
объектом в браузере илиGLOBAL
вNode.js
. Это означает, что у нас будет window.name в браузере или GLOBAL.name в Node.js с значением «George».б. Мы можем прикрепить их к объекту, так как его свойства
- Самый простой способ сделать это - изменить пустой
person
объект, например:таким образом мы можем назвать их как:
и теперь
person
объект похож на:- Другой способ прикрепить свойство к объекту - использовать
prototype
этот объект, который можно найти в любом объекте JavaScript с именем__proto__
, и я попытался объяснить это немного в итоговой части. Таким образом, мы могли бы получить аналогичный результат, выполнив:Но таким образом, что мы на самом деле делаем, это модифицируем
Object.prototype
, потому что всякий раз, когда мы создаем объект JavaScript с использованием literals ({ ... }
), он создается на основеObject.prototype
, что означает, что он присоединяется к вновь созданному объекту как именованный атрибут__proto__
, поэтому, если мы его изменим Как мы уже делали в предыдущем фрагменте кода, все объекты JavaScript будут изменены, что не является хорошей практикой. Итак, что может быть лучше практики сейчас:и теперь другие объекты в мире, но это все еще не кажется хорошей практикой. Таким образом, у нас есть еще одно решение, но чтобы использовать это решение, мы должны вернуться к той строке кода, где
person
был создан объект (var person = {};
), а затем изменить его следующим образом:он создает новый JavaScript
Object
и присоединяет егоpropertiesObject
к__proto__
атрибуту. Итак, чтобы убедиться, что вы можете сделать:Но сложность заключается в том, что у вас есть доступ ко всем свойствам, определенным
__proto__
на первом уровнеperson
объекта (подробнее см. Раздел «Сводка»).как вы видите, использование любого из этих двух способов
this
будет точно указывать наperson
объект.с. У JavaScript есть другой способ предоставить функцию
this
, которая использует call или apply для вызова функции.а также
таким образом, который мой любимый, мы можем легко вызывать наши функции как:
или
Эти 3 метода являются важными начальными шагами для выяснения функциональности .prototype.
2- Как работает
new
ключевое слово?это второй шаг для понимания
.prototype
функциональности. Это то, что я использую для имитации процесса:в этой части я попытаюсь предпринять все шаги, которые предпринимает JavaScript, без использования
new
ключевого слова иprototype
, когда вы используетеnew
ключевое слово. поэтому, когда мы это делаемnew Person("George")
,Person
функция служит конструктором. Вот что делает JavaScript, один за другим:а. во-первых, он создает пустой объект, в основном пустой хеш, например:
б. следующий шаг, который делает JavaScript, это присоединить все объекты-прототипы к вновь созданному объекту.
Здесь мы имеем
my_person_prototype
аналог объекта-прототипа.Это не тот способ, которым JavaScript на самом деле присоединяет свойства, которые определены в прототипе. Фактический путь связан с концепцией цепи прототипов.
а. & б. Вместо этих двух шагов вы можете получить точно такой же результат, выполнив:
Теперь мы можем вызвать
getName
функцию в нашемmy_person_prototype
:с. затем он передает этот объект конструктору,
мы можем сделать это с нашим образцом, как:
или
то конструктор может делать все , что хочет, потому что это внутри этого конструктора является объектом , который был только что создан.
теперь конечный результат перед имитацией других шагов: Object {name: "George"}
Резюме:
По сути, когда вы используете ключевое слово new для функции, вы вызываете ее, и эта функция служит конструктором, поэтому, когда вы говорите:
JavaScript внутренне делает объект, пустой хэш и затем он дает этот объект в конструктор, то конструктор может делать все , что хочет, потому что это внутри этого конструктора объект , который был только что создан , а затем он дает вам этот объект, конечно , если вы не использовали оператор return в своей функции или если вы поставили
return undefined;
в конце тела функции.Поэтому, когда JavaScript отправляется на поиск свойства объекта, первое, что он делает, это ищет его на этом объекте. И затем есть секретное свойство,
[[prototype]]
которое у нас обычно есть,__proto__
и это свойство - то, на что JavaScript смотрит дальше. И когда он просматривает__proto__
, поскольку он снова является другим объектом JavaScript, он имеет свой собственный__proto__
атрибут, он поднимается вверх и вверх, пока не достигнет точки, где следующий__proto__
является нулевым. Точка - единственный объект в JavaScript,__proto__
атрибут которого равен нулю, этоObject.prototype
объект:и вот как наследование работает в JavaScript.
Другими словами, когда у вас есть свойство прототипа для функции, и вы вызываете новое для него, после того, как JavaScript завершит поиск вновь созданного объекта для свойств, он пойдет смотреть на функции,
.prototype
а также возможно, что этот объект имеет свой собственный внутренний прототип. и так далее.источник
__proto__
вы сначала сотрете все свойства прототипа верхнего уровня, а затем у вас будет свежая база прототипов, которая больше не будет доступна, если вы не поделитесь ею.Семь Коанов-прототипов
Когда Чиро Сан спустился с Горы Огненной Лисы после глубокой медитации, его разум был чист и спокоен.
Его рука, однако, была беспокойной, и он сам схватил кисть и записал следующие заметки.
0) Две разные вещи можно назвать «прототипом»:
свойство прототипа, как в
obj.prototype
внутреннее свойство прототипа, обозначенное как
[[Prototype]]
в ES5 .Его можно получить через ES5
Object.getPrototypeOf()
.Firefox делает его доступным через
__proto__
свойство как расширение. ES6 теперь упоминает некоторые дополнительные требования для__proto__
.1) Эти понятия существуют, чтобы ответить на вопрос:
Интуитивно понятно, что классическое наследование должно влиять на поиск свойств.
2)
__proto__
используется для.
поиска свойства точки, как вobj.property
..prototype
это не используется для поиска непосредственно, только косвенно , как он определяет ,__proto__
при создании объекта сnew
.Порядок поиска:
obj
свойства добавлены сobj.p = ...
илиObject.defineProperty(obj, ...)
obj.__proto__
obj.__proto__.__proto__
и т. д.__proto__
естьnull
, вернитеundefined
.Это так называемая цепь прототипов .
Вы можете избежать
.
поискаobj.hasOwnProperty('key')
иObject.getOwnPropertyNames(f)
3) Есть два основных способа установки
obj.__proto__
:new
:затем
new
установил:Вот где
.prototype
привыкаешь.Object.create
:наборы:
4) код:
Соответствует следующей диаграмме (некоторые
Number
вещи опущены):Эта диаграмма показывает множество предопределенных языковых узлов объектов:
null
Object
Object.prototype
Function
Function.prototype
1
Number.prototype
(можно найти с(1).__proto__
круглыми скобками, обязательными для удовлетворения синтаксиса)Наши 2 строки кода создали только следующие новые объекты:
f
F
F.prototype
i
теперь является свойством,f
потому что, когда вы делаете:он оценивает
F
сthis
того значением , котороеnew
будет возвращать, который затем получает назначенноеf
.5)
.constructor
обычно приходитF.prototype
через.
поиск:Когда мы пишем
f.constructor
, JavaScript делает.
поиск следующим образом:f
не имеет.constructor
f.__proto__ === F.prototype
имеет.constructor === F
, так что возьмиРезультат
f.constructor == F
интуитивно правильный, такF
как используется для построенияf
, например, заданных полей, как в классических языках ООП.6) Классический синтаксис наследования может быть достигнут путем манипулирования цепочками прототипов.
ES6 добавляет
class
иextends
ключевые слова, которые в основном синтаксис для ранее возможного манипулирования прототипа безумия.Упрощенная схема без всех предопределенных объектов:
Давайте на минутку изучим, как работает следующее:
Первые наборы строк
c.i
в1
, как описано в «4)».На второй строке, когда мы делаем:
.inc
найдено по[[Prototype]]
цепочке:c
->C
->C.prototype
->inc
X.Y()
, JavaScript автоматически устанавливаетсяthis
равнымX
внутриY()
вызова функции!Та же самая логика также объясняет
d.inc
иd.inc2
.В этой статье https://javascript.info/class#not-just-a-syntax-sugar упоминаются дополнительные эффекты, которые
class
стоит знать. Некоторые из них могут быть недостижимы безclass
ключевого слова (проверьте TODO):[[FunctionKind]]:"classConstructor"
, что заставляет конструктор вызываться с новым: какой причине конструкторы класса ES6 не могут быть вызваны как нормальные функции?Object.defineProperty
.use strict
. Может быть сделано с явнойuse strict
для каждой функции, что по общему признанию утомительно.источник
.
работает поиск (и сколько копий данных сделано) , Поэтому я решил понять этот момент. Остальное - Google + сообщения в блоге + переводчик Js под рукой. :)sets f.constructor = F
часть была явно ошибочной и противоречила другим разделам:.constructor
она найдена путем.
поиска в цепочке прототипов. Исправлено сейчас.f
прототип устанавливаетсяF
только во время строительства;f
не будет знать или заботитьсяF.prototype
в любое время после его создания.prototype
позволяет делать занятия. если вы не используете,prototype
то он становится статическим.Вот короткий пример.
В приведенном выше случае у вас есть статический тест вызова функции. Эта функция доступна только через obj.test, где вы можете представить obj как класс.
где, как в приведенном ниже коде
Объект стал классом, который теперь может быть создан. Может существовать несколько экземпляров объекта obj, и все они имеют
test
функцию.Выше мое понимание. Я делаю это вики-сообществом, чтобы люди могли исправить меня, если я ошибаюсь.
источник
prototype
это свойство функций конструктора, а не экземпляров, т.е. ваш код неверен! Возможно, вы имели в виду нестандартное свойство__proto__
объектов, но это совсем другой зверь ...Прочитав эту ветку, я запутался в цепочке прототипов JavaScript, а затем нашел эти графики
http://iwiki.readthedocs.org/en/latest/javascript/js_core.html#inheritance
это четкая диаграмма, показывающая наследование JavaScript по цепочке прототипов
а также
http://www.javascriptbank.com/javascript/article/JavaScript_Classical_Inheritance/
этот содержит пример с кодом и несколько хороших диаграмм.
Надеюсь, что это также полезно для понимания цепочки прототипов JavaScript.
источник
[[Prototype]]
значит?Каждый объект имеет внутреннее свойство [[Prototype]] , связывающее его с другим объектом:
В традиционном javascript связанный объект является
prototype
свойством функции:В некоторых средах [[Prototype]] отображается как
__proto__
:Вы создаете ссылку [[Prototype]] при создании объекта.
Таким образом, эти утверждения эквивалентны:
Вы не можете увидеть ссылку target (
Object.prototype
) в новом выражении; вместо цели подразумевается конструктор (Object
).Помните:
prototype
свойство, изначально содержащее пустой объект.prototype
свойством своего конструктора.prototype
свойство останется неиспользованным.new
.источник
Object.create()
документы , @sam. Ссылки на__proto__
иObject.prototype
будут хорошими улучшениями. И мне понравились ваши примеры того, как прототипы работают с конструкторамиObject.create()
, но они, вероятно, были самой длинной и менее важной частью, от которой вы хотели избавиться.Javascript не имеет наследования в обычном смысле, но имеет цепочку прототипов.
прототип цепи
Если член объекта не может быть найден в объекте, он ищет его в цепочке прототипов. Цепочка состоит из других объектов. Доступ к прототипу данного экземпляра можно получить с помощью
__proto__
переменной. Каждый объект имеет один, так как нет никакой разницы между классами и экземплярами в javascript.Преимущество добавления функции / переменной в прототип состоит в том, что она должна быть в памяти только один раз, а не для каждого экземпляра.
Это также полезно для наследования, потому что цепочка прототипов может состоять из многих других объектов.
источник
Эта статья длинная Но я уверен, что он очистит большинство ваших запросов относительно «прототипной» природы наследования JavaScript. И даже больше. Пожалуйста, прочитайте статью полностью.
JavaScript в основном имеет два типа типов данных
Не объекты
Ниже приведены объект Номер типов данных
Эти типы данных возвращают следующее при использовании оператора typeof
typeof "строковый литерал" (или переменная, содержащая строковый литерал) === 'строка'
typeof 5 (или любой числовой литерал или переменная, содержащая числовой литерал или NaN или Infynity ) === 'число'
typeof true (или false, или переменная, содержащая true или false ) === 'логическое значение'
typeof undefined (или неопределенная переменная, или переменная, содержащая undefined ) === 'undefined'
Строка , число и булевы типы данных могут быть представлены как в виде объектов и без объектов .Когда они представлены как объекты их TypeOf всегда === «объект». Мы вернемся к этому, когда поймем типы данных объекта.
Объекты
Типы данных объекта могут быть далее разделены на два типа
Эти объекты типа Function являются те , которые возвращают строку «функцию» с TYPEOF оператором. Все пользовательские функции и все встроенные объекты JavaScript, которые могут создавать новые объекты с помощью оператора new, попадают в эту категорию. Например,
Итак, typeof (Object) === typeof (String) === typeof (Number) === typeof (Boolean) === typeof (Array) === typeof (RegExp) === typeof (Function) == = typeof (UserDefinedFunction) === 'function'
Все объекты типа Function фактически являются экземплярами встроенной функции JavaScript объекта (включая объект Function, т. Е. Он рекурсивно определен). Это как если бы эти объекты были определены следующим образом
Как уже упоминалось, объекты типа Function могут дополнительно создавать новые объекты с помощью оператора new . Например, можно создать объект типа Object , String , Number , Boolean , Array , RegExp или UserDefinedFunction с помощью
Все объекты, созданные таким образом, являются объектами типа Non Function и возвращают их typeof === 'object' . Во всех этих случаях объект «а» не может в дальнейшем создавать объекты, используя оператор new. Так что следующее неправильно
Встроенный объект Math - это typeof === 'object' . Следовательно, новый объект типа Math не может быть создан новым оператором.
Также обратите внимание, что функции Object , Array и RegExp могут создавать новый объект, даже не используя оператор new . Однако следующие не делают.
Пользовательские функции являются особым случаем.
Поскольку объекты типа Function могут создавать новые объекты, их также называют конструкторами .
Каждый конструктор / функция (встроенная или определенная пользователем) при ее определении автоматически имеет свойство «prototype» , значение которого по умолчанию устанавливается как объект. Этот объект сам по себе имеет свойство с именем «конструктор», которое по умолчанию ссылается на конструктор / функцию .
Например, когда мы определяем функцию
следующее автоматически происходит
Это свойство «prototype» присутствует только в объектах типа Function (и никогда в объектах типа Non Function ).
Это связано с тем, что при создании нового объекта (с использованием оператора new) он наследует все свойства и методы от текущего объекта-прототипа функции Constructor, т. Е. Во вновь созданном объекте создается внутренняя ссылка, которая ссылается на объект, на который ссылается текущий объект-прототип функции Constructor.
Эта «внутренняя ссылка», которая создается в объекте для ссылки на унаследованные свойства, называется прототипом объекта (который ссылается на объект, на который ссылается свойство «прототипа» конструктора, но отличается от него). Для любого объекта (Function или Non Function) это можно получить с помощью метода Object.getPrototypeOf () . С помощью этого метода можно проследить цепочку прототипов объекта.
Кроме того, каждый создаваемый объект ( тип функции или тип не функции ) имеет свойство «конструктор», которое наследуется от объекта, на который ссылается свойство prototype функции «Конструктор». По умолчанию это свойство «конструктор» ссылается на функцию «Конструктор», которая его создала (если «прототип» функции конструктора по умолчанию не изменился).
Для всех объектов типа Function функция конструктора всегда является функцией Function () {}
Для объектов не-функционального типа (например, Javascript Built in Math object) функция конструктора - это функция, которая его создала. Для объекта Math это функция Object () {} .
Вся концепция, объясненная выше, может быть немного сложной для понимания без какого-либо вспомогательного кода. Пожалуйста, просмотрите следующий код построчно, чтобы понять концепцию. Попробуйте выполнить это, чтобы иметь лучшее понимание.
Цепочка прототипов каждого объекта в конечном итоге ведет к объекту Object.prototype (который сам по себе не имеет какого-либо объекта-прототипа). Следующий код может быть использован для отслеживания цепочки прототипов объекта
Прототип цепи для различных объектов работает следующим образом.
Для создания объекта без прототипа используйте следующее:
Можно подумать, что установив свойство prototype конструктора NULL, создаст объект с нулевым прототипом. Однако в таких случаях прототип вновь созданного объекта устанавливается в Object.prototype, а его конструктор устанавливается в функцию Object. Это демонстрируется следующим кодом
Следуя в кратком изложении этой статьи
Только объекты типа Function могут создавать новый объект, используя оператор new . Созданные таким образом объекты являются объектами не функционального типа . Эти объекты типа Function Номера не могут дополнительно создать объект с помощью оператора нового .
Все объекты типа Function по умолчанию имеют свойство «prototype» . Это свойство «prototype» ссылается на объект, имеющий свойство «constructor», которое по умолчанию ссылается на сам объект типа Function .
Все объекты ( типа Function и Non типа Function ) имеют свойство «конструктор» , который по умолчанию ссылки Тип функции объекта / Конструктор , который создал его.
Каждый создаваемый объект внутренне ссылается на объект, на который ссылается свойство «prototype» конструктора, который его создал. Этот объект известен как прототип созданного объекта (который отличается от свойства «prototype» объектов функции, на которое он ссылается). Таким образом, созданный объект может напрямую обращаться к методам и свойствам, определенным в объекте, на который ссылается свойство «прототипа» конструктора (во время создания объекта).
An прототип объекта (и , следовательно , его наследственные имена свойств) могут быть получены с помощью Object.getPrototypeOf () метод. Фактически этот метод может использоваться для навигации по всей цепочке прототипов объекта.
Цепочка прототипов каждого объекта в конечном итоге ведет к объекту Object.prototype (если объект не создан с использованием Object.create (null), в этом случае у объекта нет прототипа).
typeof (new Array ()) === «объект» создан по типу языка и не является ошибкой, на что указывает Дуглас Крокфорд
Установка для свойства prototype конструктора значения null (или неопределенного, number, true, false, string) не должна создавать объект с нулевым прототипом. В таких случаях для вновь созданного прототипа объекта устанавливается Object.prototype, а для его конструктора устанавливается функция Object.
Надеюсь это поможет.
источник
Концепция
prototypal
наследования является одной из самых сложных для многих разработчиков. Давайте попробуем понять корень проблемы, чтобы понятьprototypal inheritance
лучше. Давайте начнем сplain
функции.Если мы используем
new
оператор вTree function
, мы вызываем его какconstructor
функцию.Каждая
JavaScript
функция имеетprototype
. КогдаTree.prototype
вы входите, вы получаете ...Если вы посмотрите на приведенный выше
console.log()
вывод, вы можете увидеть свойство конструктораTree.prototype
и__proto__
свойство тоже. Представление__proto__
представляет собойprototype
то, что этоfunction
основано, и, поскольку это просто равнина, ещеJavaScript function
неinheritance
настроенная, оно ссылается на то,Object prototype
что просто встроено в JavaScript ...https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/prototype
Это имеет такие вещи, как и
.toString, .toValue, .hasOwnProperty
т.д ...__proto__
который был принесен мой Mozilla устарела и замененаObject.getPrototypeOf
методом, чтобы получитьobject's prototype
.Давайте добавим метод к нашему
Tree
prototype
.Мы изменили
Root
и добавилиfunction
ветку к нему.Это означает, что когда вы создаете
instance
ofTree
, вы можете вызвать егоbranch
метод.Мы также можем добавить
primitives
илиobjects
к нашемуPrototype
.Давайте добавим
child-tree
к нашемуTree
.Здесь он
Child
наследует егоprototype
от Tree. То, что мы здесь делаем, используетObject.create()
метод для создания нового объекта на основе того, что вы передаете, вот оноTree.prototype
. В этом случае мы устанавливаем прототип Child для нового объекта, который выглядит идентичноTree
прототипу. Далее мы устанавливаемChild's constructor to Child
, если мы этого не сделаем, это будет указыватьTree()
.Child
теперь имеет своиprototype
, свои__proto__
точкиTree
иTree's prototype
указывает на базуObject
.Теперь вы создаете
instance
изChild
и вызова ,branch
который первоначально доступен вTree
. Мы на самом деле не определили нашиbranch
наChild prototype
. НО, вRoot prototype
котором ребенок наследует.В JS все не является объектом, все может действовать как объект.
Javascript
имеет такие примитивы, какstrings, number, booleans, undefined, null.
ониobject(i.e reference types)
, но, безусловно, может действовать какobject
. Давайте посмотрим на пример здесь.В первой строке этого списка
primitive
строковому значению присваивается имя. Вторая строка обрабатывает имя какobject
и вызываетcharAt(0)
с использованием точечной нотации.Вот что происходит за кулисами: // что
JavaScript
делает двигательString object
Существует только одно заявление , прежде чем он будет уничтожен (этот процесс называетсяautoboxing
). Давайте снова вернемся к нашемуprototypal
inheritance
.Javascript
поддерживает наследование черезdelegation
на основеprototypes
.Function
естьprototype
свойство, которое относится к другому объекту.properties/functions
смотрят изobject
себя или черезprototype
цепочку, если она не существуетА
prototype
в JS - это объект, которыйyields
вы родителю другогоobject
. [то есть .. делегирование]Delegation
означает, что если вы не можете что-то сделать, вы скажете кому-то другому сделать это за вас.https://jsfiddle.net/say0tzpL/1/
Если вы посмотрите вышеупомянутую скрипку, у собаки есть доступ к
toString
методу, но он не доступен в нем, но доступен через цепочку прототипов, которая делегируетObject.prototype
Если вы посмотрите на приведенный ниже, мы пытаемся получить доступ к
call
методу, доступному в каждомfunction
.https://jsfiddle.net/rknffckc/
Если вы посмотрите вышеупомянутую скрипку, у
Profile
функции есть доступ кcall
методу, но он не доступен в нем, но доступен через цепочку прототипов, которая делегируетFunction.prototype
Примечание:
prototype
это свойство конструктора функции, тогда__proto__
как это свойство объектов, созданных из конструктора функции. Каждая функция поставляется соprototype
свойством, значение которого является пустымobject
. Когда мы создаем экземпляр функции, мы получаем внутреннее свойство[[Prototype]]
или__proto__
ссылка на который является прототипом функцииconstructor
.Вышеприведенная диаграмма выглядит немного сложнее, но выявляет всю картину того, как
prototype chaining
работает. Давайте пройдемся по этому медленно:Есть два экземпляра
b1
иb2
, чей конструкторBar
и родитель - Foo и имеет два метода из цепочки прототиповidentify
иspeak
черезBar
иFoo
https://jsfiddle.net/kbp7jr7n/
Если вы посмотрите код выше, у нас есть
Foo
конструктор, у которого есть метод,identify()
иBar
конструктор, у которого естьspeak
метод. Мы создаем дваBar
экземпляраb1
иb2
родительский тип которыхFoo
. Теперь при вызовеspeak
методаBar
мы можем определить, кто вызывает разговор поprototype
цепочке.Bar
Теперь есть все методы,Foo
которые определены в егоprototype
. Давайте копать дальше в пониманииObject.prototype
иFunction.prototype
и как они связаны между собой . Если вы посмотрите вверх конструкторFoo
,Bar
иObject
естьFunction constructor
.prototype
ИзBar
этоFoo
,prototype
изFoo
ISObject
и если вы посмотрите тесноprototype
отFoo
связаноObject.prototype
.Прежде чем мы закроем это, давайте просто завернем небольшой кусочек кода здесь, чтобы подвести итог всему выше . Здесь мы используем
instanceof
оператор, чтобы проверить,object
имеет ли an в своейprototype
цепочкеprototype
свойство a,constructor
которое ниже суммирует всю большую диаграмму.Я надеюсь, что это дополнение немного информации, я знаю, что это может быть большим, чтобы понять ... простыми словами, это просто объекты, связанные с объектами !!!!
источник
Интерфейс к стандартным классам становится расширяемым. Например, вы используете
Array
класс, и вам также необходимо добавить настраиваемый сериализатор для всех ваших объектов массива. Вы бы потратили время на кодирование подкласса, или использовали состав или ... Свойство prototype решает эту проблему, позволяя пользователям контролировать точный набор членов / методов, доступных для класса.Думайте о прототипах как о дополнительном vtable-указателе. Когда некоторые члены отсутствуют в исходном классе, прототип ищется во время выполнения.
источник
Это может помочь классифицировать цепочки прототипов на две категории.
Рассмотрим конструктор:
Значение
Object.getPrototypeOf(Person)
является функцией. На самом деле это такFunction.prototype
. Поскольку онPerson
был создан как функция, он разделяет тот же объект функции-прототип, что и все функции. Это так же, какPerson.__proto__
, но это свойство не должно использоваться. Во всяком случае, сObject.getPrototypeOf(Person)
вами эффективно подниматься по лестнице того, что называется цепочкой прототипов.Цепочка в направлении вверх выглядит так:
Person
→Function.prototype
→Object.prototype
(конечная точка)Важно то, что эта цепочка прототипов имеет мало общего с объектами, которые
Person
можно построить . Эти сконструированные объекты имеют свою собственную цепочку прототипов, и эта цепочка потенциально может не иметь близких предков, общих с упомянутой выше.Возьмем для примера этот объект:
р не имеет прямого отношения прототипа с человеком . Их отношения разные. Объект р имеет свою собственную цепочку прототипов. Используя
Object.getPrototypeOf
, вы найдете цепочку следующим образом:p
→Person.prototype
→Object.prototype
(конечная точка)В этой цепочке нет объекта функции (хотя это может быть).
Так что,
Person
похоже, связано с двумя видами цепей, которые живут своей жизнью. Чтобы «перепрыгнуть» из одной цепочки в другую, вы используете:.prototype
: перейти от цепочки конструктора к цепочке созданного объекта. Таким образом, это свойство определено только для функциональных объектов (какnew
может использоваться только для функций)..constructor
: перейти от цепочки созданного объекта к цепочке конструктора.Вот визуальное представление двух задействованных цепочек прототипов, представленных в виде столбцов:
Чтобы обобщить:
Неудивительно, что название объекта
prototype
может привести к путанице. Возможно, было бы яснее, если бы это свойство было названоprototypeOfConstructedInstances
или что-то в этом роде.Вы можете прыгать туда-сюда между двумя цепями прототипов:
Эту симметрию можно нарушить, явно назначив другой объект
prototype
свойству (подробнее об этом позже).Создать одну функцию, получить два объекта
Person.prototype
является объектом, который был создан одновременно сPerson
созданием функции . Он имеетPerson
конструктор, хотя этот конструктор еще не выполнялся. Итак, два объекта создаются одновременно:Person
сама по себеОба являются объектами, но они играют разные роли: функции объекта конструкций , в то время как другой объект представляет собой прототип любого объекта, функция будет строить. Прототип объекта станет родителем построенного объекта в его цепочке прототипов.
Поскольку функция также является объектом, она также имеет своего родителя в собственной цепочке прототипов, но следует помнить, что эти две цепочки связаны с разными вещами.
Вот некоторые равенства, которые могут помочь разобраться в проблеме - все это напечатано
true
:Добавление уровней в цепочку прототипов
Хотя объект-прототип создается при создании функции конструктора, вы можете игнорировать этот объект и назначить другой объект, который следует использовать в качестве прототипа для любых последующих экземпляров, созданных этим конструктором.
Например:
Теперь цепочка прототипов t на один шаг длиннее, чем цепочка p :
t
→p
→Person.prototype
→Object.prototype
(конечная точка)Другая цепочка прототипов не длиннее:
Thief
и уPerson
братьев и сестер общий родительский элемент в их цепочке прототипов:Person
}Thief
} →Function.prototype
→Object.prototype
(конечная точка)Ранее представленный рисунок может быть расширен до этого (оригинал
Thief.prototype
опущен):Синие линии представляют цепочки прототипов, другие цветные линии представляют другие отношения:
источник
Полное руководство по объектно-ориентированному JavaScript - очень краткое и четкое объяснение задаваемого вопроса о видео в течение 30 минут (тема наследования прототипа начинается с 5:45 , хотя я бы предпочел прослушать все видео). Автор этого видео также сделал сайт визуализатора объектов JavaScript http://www.objectplayground.com/ .
источник
Я нашел полезным объяснить «цепочку прототипов» как рекурсивное соглашение, когда
obj_n.prop_X
на него ссылаются:если
obj_n.prop_X
не существует, проверьтеobj_n+1.prop_X
гдеobj_n+1 = obj_n.[[prototype]]
Если
prop_X
наконец найден в k-ом объекте-прототипе, тоobj_1.prop_X = obj_1.[[prototype]].[[prototype]]..(k-times)..[[prototype]].prop_X
Вы можете найти график отношений объектов Javascript по их свойствам здесь:
http://jsobjects.org
источник
Когда конструктор создает объект, этот объект неявно ссылается на свойство «прототипа» конструктора с целью разрешения ссылок на свойства. На свойство «prototype» конструктора может ссылаться выражение программы constructor.prototype, а свойства, добавленные к прототипу объекта, совместно используются посредством наследования всеми объектами, совместно использующими прототип.
источник
Здесь есть две разные, но связанные сущности, которые нужно объяснить:
.prototype
Свойство функций.[[Prototype]]
[1] всех объектов [2] .Это две разные вещи.
[[Prototype]]
Свойство:Это свойство, которое существует на всех [2] объектах.
Здесь хранится еще один объект, который, как сам объект, имеет
[[Prototype]]
свой собственный объект, который указывает на другой объект. Этот другой объект имеет[[Prototype]]
свой собственный. Эта история продолжается до тех пор, пока вы не достигнете прототипа объекта, который предоставляет методы, доступные для всех объектов (например,.toString
).[[Prototype]]
Свойство является частью того , что образует[[Prototype]]
цепь. Эта цепочка[[Prototype]]
объектов является то , что проверяется , когда, например,[[Get]]
или[[Set]]
операции выполняются на объекте:.prototype
Свойство:Это свойство, которое можно найти только в функциях. Используя очень простую функцию:
.prototype
Свойство содержит объект , который будет назначен ,b.[[Prototype]]
когда вы делаетеvar b = new Bar
. Вы можете легко проверить это:Одним из наиболее важных
.prototype
с является то , что вObject
функции . Этот прототип содержит прототип объекта, который[[Prototype]]
содержится во всех цепях. На нем определены все доступные методы для новых объектов:Теперь, поскольку
.prototype
это объект, у него есть[[Prototype]]
свойство. Когда вы не делаете никаких назначенийFunction.prototype
,.prototype
's[[Prototype]]
указывает на прототип объекта (Object.prototype
). Это автоматически выполняется каждый раз, когда вы создаете новую функцию.Таким образом, каждый раз, когда вы делаете
new Bar;
цепочку прототипов, вы получаете все, что определено,Bar.prototype
и все, что определеноObject.prototype
:Когда вы делаете назначения
Function.prototype
всем, что делаете, расширяете цепочку прототипов, чтобы включить в нее другой объект. Это как вставка в односвязный список.Это в основном изменяет
[[Prototype]]
цепочку, позволяя свойствам, которые определены для объекта, назначенногоFunction.prototype
быть видимым любому объекту, созданному функцией.[1: Это никого не смущает; доступны через в
__proto__
собственности во многих реализациях.[2]: все, кроме
null
.источник
Позвольте мне рассказать вам о моем понимании прототипов. Я не собираюсь сравнивать наследование здесь с другими языками. Я бы хотел, чтобы люди перестали сравнивать языки и просто понимали язык как таковой. Понимание прототипов и наследования прототипов так просто, как я покажу вам ниже.
Прототип похож на модель, на основе которой вы создаете продукт. Важно понять, что при создании объекта с использованием другого объекта в качестве прототипа связь между прототипом и продуктом является постоянной. Например:
Каждый объект содержит внутреннее свойство, называемое [[prototype]], к которому может обращаться
Object.getPrototypeOf()
функция.Object.create(model)
создает новый объект и устанавливает его свойство [[prototype]] для объектной модели . Следовательно, когда вы это сделаетеObject.getPrototypeOf(product)
, вы получите объектную модель .Свойства в продукте обрабатываются следующим образом:
Такое связывание объектов с использованием свойства prototype называется наследованием прототипов. Там это так просто, согласен?
источник
Еще одна попытка объяснить наследование на основе прототипов JavaScript с помощью более качественных изображений
источник
Рассмотрим следующий
keyValueStore
объект:Я могу создать новый экземпляр этого объекта, выполнив это:
Каждый экземпляр этого объекта будет иметь следующие открытые свойства:
data
get
set
delete
getLength
Теперь предположим, что мы создали 100 экземпляров этого
keyValueStore
объекта. Несмотря на тоget
,set
,delete
,getLength
будет делать ту же самую вещь для каждого из этих 100 экземпляров, каждый экземпляр имеет свою собственную копию этой функции.Теперь представьте, что если бы вы могли иметь только один
get
,set
,delete
иgetLength
копию, и каждый экземпляр будет ссылаться на ту же самую функцию. Это было бы лучше для производительности и потребовало бы меньше памяти.Вот где появляются прототипы. Прототип - это «план» свойств, который наследуется, но не копируется экземплярами. Таким образом, это означает, что он существует только один раз в памяти для всех экземпляров объекта и является общим для всех этих экземпляров.
Теперь рассмотрим
keyValueStore
объект еще раз. Я мог бы переписать это так:Это точно так же, как и в предыдущей версии
keyValueStore
объекта, за исключением того, что все его методы теперь помещены в прототип. Это означает, что все 100 экземпляров теперь совместно используют эти четыре метода вместо того, чтобы каждый имел свою собственную копию.источник
Резюме:
new
ключевым словом, объект получает прототип. Ссылку на этот прототип можно найти на__proto__
свойстве вновь созданного объекта.__proto__
свойство ссылается наprototype
свойство функции конструктора.Пример:
Почему это полезно:
Javascript имеет механизм поиска свойств в объектах, который называется «наследование прототипов» , вот что в основном делает:
Например:
Обновить:
Это
__proto__
свойство устарело, хотя оно реализовано в большинстве современных браузеров, и лучший способ получить ссылку на объект-прототип:Object.getPrototypeOf()
источник
Мне всегда нравятся аналогии, когда дело доходит до понимания такого рода вещей. На мой взгляд, «прототипическое наследование» довольно запутанно по сравнению с классовым наследованием басов, хотя прототипы - это гораздо более простая парадигма. На самом деле с прототипами наследования действительно не существует, поэтому само название вводит в заблуждение, это скорее своего рода «делегирование».
Представь себе это ....
Ты в старшей школе, ты в классе и у тебя есть тест, который должен пройти сегодня, но у тебя нет ручки, чтобы заполнить свои ответы. Doh!
Вы сидите рядом со своим другом Финниусом, у которого может быть ручка. Вы спрашиваете, и он безуспешно оглядывает свой стол, но вместо того, чтобы сказать «у меня нет ручки», он хороший друг, он проверяет у своего друга Дерпа, есть ли у него ручка. У Дерпа действительно есть запасная ручка, и он передает ее Финниусу, который передает ее вам, чтобы завершить тест. Дерп доверил ручку Финниусу, который делегировал вам ручку для использования.
Здесь важно то, что Дерп не дает вам ручку, поскольку у вас нет с ним прямых отношений .
Это упрощенный пример того, как работают прототипы, где дерево данных ищет то, что вы ищете.
источник
другая схема, показывающая __proto__ , отношения между прототипом и конструктором :
источник
Просто у вас уже есть объект,
Object.new
но у вас все еще нет объекта при использовании синтаксиса конструктора.источник
Ссылка: https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object_prototypes
источник
Прототип создает новый объект путем клонирования существующего объекта . Так что на самом деле, когда мы думаем о прототипе, мы можем подумать о клонировании или создании чего-то, а не о создании.
источник