Это очень простая объектная модель на основе прототипа, которая будет рассмотрена в качестве примера при объяснении без комментариев:
function Person(name){
this.name = name;
}
Person.prototype.getName = function(){
console.log(this.name);
}
var person = new Person("George");
Есть несколько важных моментов, которые мы должны рассмотреть, прежде чем перейти к концепции прототипа.
1- Как на самом деле работают функции JavaScript:
Чтобы сделать первый шаг, мы должны выяснить, как на самом деле работают функции JavaScript, как функции класса this ключевого слова в ней или просто как обычную функцию со своими аргументами, что она делает и что возвращает.
Допустим, мы хотим создать Personобъектную модель. но в этом шаге я буду пытаться сделать то же самое точное без использования prototypeи newключевых слов .
Таким образом , в этом шаге functions, objectsиthis ключевое слово, все у нас есть.
Первый вопрос заключается в том, как thisключевое слово может быть полезным без использования newключевого слова. .
Итак, чтобы ответить на это, скажем, у нас есть пустой объект и две функции, такие как:
var person = {};
function Person(name){ this.name = name; }
function getName(){
console.log(this.name);
}
и теперь без использования newключевого слова как мы могли бы использовать эти функции. Таким образом, у JavaScript есть 3 различных способа сделать это:
а. Первый способ - просто вызвать функцию как обычную функцию:
Person("George");
getName();//would print the "George" in the console
в этом случае это будет текущий объект контекста, который обычно является глобальным windowобъектом в браузере или GLOBALвNode.js . Это означает, что у нас будет window.name в браузере или GLOBAL.name в Node.js с значением «George».
б. Мы можем прикрепить их к объекту, так как его свойства
- Самый простой способ сделать это - изменить пустой personобъект, например:
person.Person = Person;
person.getName = getName;
таким образом мы можем назвать их как:
person.Person("George");
person.getName();// -->"George"
и теперь personобъект похож на:
Object {Person: function, getName: function, name: "George"}
- Другой способ прикрепить свойство к объекту - использовать prototypeэтот объект, который можно найти в любом объекте JavaScript с именем __proto__, и я попытался объяснить это немного в итоговой части. Таким образом, мы могли бы получить аналогичный результат, выполнив:
person.__proto__.Person = Person;
person.__proto__.getName = getName;
Но таким образом, что мы на самом деле делаем, это модифицируем Object.prototype, потому что всякий раз, когда мы создаем объект JavaScript с использованием literals ( { ... }), он создается на основе Object.prototype, что означает, что он присоединяется к вновь созданному объекту как именованный атрибут __proto__, поэтому, если мы его изменим Как мы уже делали в предыдущем фрагменте кода, все объекты JavaScript будут изменены, что не является хорошей практикой. Итак, что может быть лучше практики сейчас:
person.__proto__ = {
Person: Person,
getName: getName
};
и теперь другие объекты в мире, но это все еще не кажется хорошей практикой. Таким образом, у нас есть еще одно решение, но чтобы использовать это решение, мы должны вернуться к той строке кода, где personбыл создан объект ( var person = {};), а затем изменить его следующим образом:
var propertiesObject = {
Person: Person,
getName: getName
};
var person = Object.create(propertiesObject);
он создает новый JavaScript Objectи присоединяет его propertiesObjectк __proto__атрибуту. Итак, чтобы убедиться, что вы можете сделать:
console.log(person.__proto__===propertiesObject); //true
Но сложность заключается в том, что у вас есть доступ ко всем свойствам, определенным __proto__на первом уровне personобъекта (подробнее см. Раздел «Сводка»).
как вы видите, использование любого из этих двух способов thisбудет точно указывать на personобъект.
с. У JavaScript есть другой способ предоставить функцию this, которая использует call или apply для вызова функции.
Метод apply () вызывает функцию с заданным значением this и аргументами, представленными в виде массива (или объекта, подобного массиву).
а также
Метод call () вызывает функцию с заданным значением this и аргументами, предоставляемыми индивидуально.
таким образом, который мой любимый, мы можем легко вызывать наши функции как:
Person.call(person, "George");
или
//apply is more useful when params count is not fixed
Person.apply(person, ["George"]);
getName.call(person);
getName.apply(person);
Эти 3 метода являются важными начальными шагами для выяснения функциональности .prototype.
2- Как работает newключевое слово?
это второй шаг для понимания .prototypeфункциональности. Это то, что я использую для имитации процесса:
function Person(name){ this.name = name; }
my_person_prototype = { getName: function(){ console.log(this.name); } };
в этой части я попытаюсь предпринять все шаги, которые предпринимает JavaScript, без использования newключевого слова и prototype, когда вы используете newключевое слово. поэтому, когда мы это делаем new Person("George"), Personфункция служит конструктором. Вот что делает JavaScript, один за другим:
а. во-первых, он создает пустой объект, в основном пустой хеш, например:
var newObject = {};
б. следующий шаг, который делает JavaScript, это присоединить все объекты-прототипы к вновь созданному объекту.
Здесь мы имеем my_person_prototypeаналог объекта-прототипа.
for(var key in my_person_prototype){
newObject[key] = my_person_prototype[key];
}
Это не тот способ, которым JavaScript на самом деле присоединяет свойства, которые определены в прототипе. Фактический путь связан с концепцией цепи прототипов.
а. & б. Вместо этих двух шагов вы можете получить точно такой же результат, выполнив:
var newObject = Object.create(my_person_prototype);
//here you can check out the __proto__ attribute
console.log(newObject.__proto__ === my_person_prototype); //true
//and also check if you have access to your desired properties
console.log(typeof newObject.getName);//"function"
Теперь мы можем вызвать getNameфункцию в нашем my_person_prototype:
newObject.getName();
с. затем он передает этот объект конструктору,
мы можем сделать это с нашим образцом, как:
Person.call(newObject, "George");
или
Person.apply(newObject, ["George"]);
то конструктор может делать все , что хочет, потому что это внутри этого конструктора является объектом , который был только что создан.
теперь конечный результат перед имитацией других шагов: Object {name: "George"}
Резюме:
По сути, когда вы используете ключевое слово new для функции, вы вызываете ее, и эта функция служит конструктором, поэтому, когда вы говорите:
new FunctionName()
JavaScript внутренне делает объект, пустой хэш и затем он дает этот объект в конструктор, то конструктор может делать все , что хочет, потому что это внутри этого конструктора объект , который был только что создан , а затем он дает вам этот объект, конечно , если вы не использовали оператор return в своей функции или если вы поставили return undefined;в конце тела функции.
Поэтому, когда JavaScript отправляется на поиск свойства объекта, первое, что он делает, это ищет его на этом объекте. И затем есть секретное свойство, [[prototype]]которое у нас обычно есть, __proto__и это свойство - то, на что JavaScript смотрит дальше. И когда он просматривает __proto__, поскольку он снова является другим объектом JavaScript, он имеет свой собственный __proto__атрибут, он поднимается вверх и вверх, пока не достигнет точки, где следующий __proto__является нулевым. Точка - единственный объект в JavaScript, __proto__атрибут которого равен нулю, это Object.prototypeобъект:
console.log(Object.prototype.__proto__===null);//true
и вот как наследование работает в JavaScript.

Другими словами, когда у вас есть свойство прототипа для функции, и вы вызываете новое для него, после того, как JavaScript завершит поиск вновь созданного объекта для свойств, он пойдет смотреть на функции, .prototypeа также возможно, что этот объект имеет свой собственный внутренний прототип. и так далее.