Все функции Javascript имеют объект-прототип, который допускает прототипное наследование (объекты наследуются непосредственно от других объектов). Давайте взглянем.
function Example(){ return 2 }Example.prototype // {constructor: ƒ}Example.protoype.name = 'An example'Example.prototype // {name: 'An example', constructor: ƒ}Example.name // 'An example'
И наоборот, все объекты имеют свойство __proto__, и *оно будет указывать на прототип их функции-конструктора*.
let exampleOne = new ExampleexampleOne // {}exampleOne.__proto__ // {name: 'An example', constructor: ƒ}exampleOne.name // 'An example'
Это важное откровение! Сам exampleOne является пустым объектом, но его __proto__ может указывать на объект-прототип, содержащий свойство name. Когда вызывается exampleOne.name, Javascript сначала проверяет, есть ли у exampleOne свойство с именем name. Это не так, поэтому Javascript будет искать в своем объекте __proto__ свойство name, которое он находит!
Используя прототип, объекты Javascript не наследуют напрямую свойства функций-конструкторов. Они просто имеют доступ к объекту-прототипу своей функции-конструктора.*
Вот почему мы можем создать объект, добавить новое свойство в прототип функции, и объект будет иметь к нему доступ; конечно, это имело бы смысл теперь, когда мы знаем, что свойство __proto__ объекта указывает на этот объект-прототип, но в противном случае это казалось бы волшебством, потому что эта функциональность не может быть реализована в классическом наследовании.
Чтобы еще больше доказать нашу точку зрения, мы можем создать еще один объект из нашего примера функции и сравнить их свойства __proto__.
let exampleTwo = new Example{name: 'not same object'} === {name: 'not same object'} // falseexampleTwo.__proto__ === exampleTwo.proto__ // true
Два объекта __proto__ равны, то есть они указывают на *один и тот же объект в памяти*: прототип функции примера. На самом деле, сама функция не имеет значения, потому что это не класс; имеет значение только объект-прототип. Докажем это сейчас.
Example = 'Function Gone!'Example.prototype // undefinedexampleOne.__proto__ // {name: 'An example', constructor: ƒ}
Мы изменили функцию, но объект-прототип все еще существует в памяти, поэтому наши объекты все еще имеют ссылку на него. Как мы видим, прототипы — это мощный инструмент наследования свойств. Имея только один объект-прототип, содержащий наши свойства, нашим экземплярам нет необходимости иметь собственный дублирующий набор одних и тех же свойств (если, конечно, мы не хотим внести коррективы в функциональность нашего объекта, и в этом случае мы можем просто дать объекта новое свойство, чтобы переопределить свойство в объекте-прототипе!).