Это очень простая объектная модель на основе прототипа, которая будет рассмотрена в качестве примера при объяснении без комментариев:
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
а также возможно, что этот объект имеет свой собственный внутренний прототип. и так далее.