Чотири правила
Найпростіший спосіб розібратися з оператором new
– зрозуміти , що саме він робить. При використанні new
, потрібно пам'ятати чотири правила:
- Він створює новий порожній об'єкт.
- Він зв'язує this з нашим новим об'єктом.
- Він додає властивість до новоствореного об'єкта під назвою «proto», який вказує на об'єкт-прототип функції-конструктора .
- Він додає
return this
в кінець функції, так що об'єкт, що створюється, повертається з функції.
Зачекайте, що?
Це нормально, якщо ці правила досі для вас незрозумілі. Давайте почнемо з простого. Створимо функцію-конструктор з ім'ям Student
. Ця функція буде приймати два параметри: name
і age
. Функція буде встановлювати властивості через значення this
.
function Student(name, age) {
this.name = name;
this.age = age;
}
Чудово. Викличемо наш конструктор з оператором new
. Ми будемо передавати два аргументи: 'John'
і 26
.
var first = new Student('John', 26);
Отже, що ж відбувається, коли ми запустимо даний код?
- Створюється новий об'єкт –
first
. this
прив'язується доfirst
. Тому будь-які посилання наthis
вкажуть наfirst
.- Також додається proto.
first. ____proto____
вказуватиме наStudent.prototype
. - Після цього наш новий
first
об'єкт повертається як новаfirst
змінна.
Тепер ми можемо виконати кілька console.log
, щоб перевірити, чи все працює вірно:
console.log(first.name);
// John
console.log(first.age);
// 26
Прекрасно. Давайте заглибимося більше в ____proto____
.
Прототипи
Кожен об'єкт JavaScript має прототип. Всі об'єкти в JavaScript успадковують властивості і методи від своїх прототипів.
Давайте подивимося на приклад. Відкрийте ваш Chrome Developer Console (Windows: Ctrl + Shift + J) (Mac: Cmd + Option + J) і введіть функцію Student
, яку уже було нещодавно показано:
function Student(name, age) {
this.name = name;
this.age = age;
}
Щоб показати, що, дійсно, кожен об'єкт має прототип, давайте напишемо:
Student.prototype;
// Object {...}
Добре, як бачимо, повертається об'єкт. Тепер давайте спробуємо створити нового студента:
var second = new Student('Jeff', 50);
Ми використали конструктор-функцію Student
для створення нашого другого студента по імені Джефф. І, так як ми використали оператор new
, то __proto__
має бути також додана в наш second
об'єкт. Ця властивість повинна вказувати на батьківський конструктор. Спробуємо побачити, чи вони рівні:
second.__proto__ === Student.prototype;
// true
Нарешті, наш Student.prototype.constructor
повинен вказати на нашу функцію-конструктор Student:
Student.prototype.constructor;
// function Student(name, age) {
// this.name = name;
// this.age = age;
// }
Стало складно, чи не так? Давайте подивимося на малюнок, можливо, він допоможе вам зрозуміти, що відбувається:
Наша функція-конструктор 'Student' (а також всі інші функції-конструктори) має властивість '.prototype'. Цей прототип містить в собі об'єкт з назвою '.constructor', який вказує назад на функцію-конструктора. Це такий ось невеликий цикл. Потім, коли ми використовуємо оператор 'new', щоб створити новий об'єкт, кожен об'єкт отримує .____proto____
–властивість , яка пов'язує новий об'єкт назад з Student.prototype
.
Так чому ж це так важливо?
Це важливо через таку річ, як успадкування. Об'єкт-прототип є загальним для всіх об'єктів , створених з допомогою цієї функції-конструктора. Це означає , що ми можемо додати функції і властивості до прототипу, так що всі наші об'єкти зможуть їх використовувати.
У наших прикладах вище, ми створили тільки два об'єкти Student, але що, якщо замість двох студентів, у нас є 20000? Таким чином, ми зможемо зекономити тонну обчислювальної потужності, реалізувавши загальновикористовувані функції у прототипі , а не в кожному зі студентських об'єктів.
Давайте подивимося на приклад, щоб було більш зрозуміло. В консолі додайте наступний рядок:
Student.prototype.sayInfo = function(){
console.log(this.name + ' is ' + this.age + ' years old');
}
Знову ж таки, що ми тут робимо? Додаємо функцію до студентського прототипу – будь-який студент , якого ми створюємо або створили в даний час має доступ до цієї нової .sayInfo
функції! Давайте перевіримо це:
second.sayInfo();
// Jeff is 50 years old
Додамо нового студента і спробуємо ще раз:
var third = new Student('Tracy', 15);
third;
// Student {name: "Tracy", age: 15}
third.sayInfo();
// Tracy is 15 years old
Це працює! Вся справа в успадкуванні. Об'єкт спочатку перевірятиме, чи він має властивість, яку ми викликаємо. Якщо їні, він перейде до прототипу, і запитає: «А у тебе є ця властивість?». Це продовжуватиметься, поки ми або не знайдемо цю властивість, або не досягнемо кіньця ланцюжка прототипів.
За допомогою успадкування ви можете використовувати такі функції , як .toString(). Подумайте про це. Вам зовсім не потрібно буде писати toString() метод, ви зможете використати його просто так. Це тому, що цей метод, а також інші вбудовані в методи JS знаходяться в Object prototype. Кожен об'єкт, який ми створюємо, в кінці-кінців делегується Object prototype. І, звичайно, ми могли б переписати ці методи, наприклад, на щось на зразок цього:
var name = {
toString: function(){
console.log('Not a good idea');
}
};
name.toString();
// Not a good idea
Перш ніж перейти до прототипу, наш об'єкт спочатку перевіряє, чи він має даний метод. Так як у нас є метод, він запускається і успадкування не потрібне. Але це не дуже гарна ідея. Залишайте глобальні методи такими, якими вони є, і називайте ваші функції інакше.
Висновок
Для нового розробника це може бути дуже жорстка концепція, але як тільки ви одного разу спробуєте, ви зможете написати набагато кращий, чистіший код. За допомогою прототипів ми можемо поширювати одні і ті ж фрагменти коду через сотні, навіть тисячі об'єктів швидко і ефективно. Ідея цієї статті – надати вам базу знань, щоб ви продовжили навчання самостійно.
Ще немає коментарів