ООП і спадкування в javascript - popel agency

Класи? Ні не чув

Код вище дає нам об'єкт з ім'ям myCar і методами honk () і drive ().

Але що якщо нам потрібно створити 30 об'єктів? Як уникнути створення всіх методів для кожного об'єкта вручну? У реальному світі для масового виробництва будь-яких речей використовуються спеціальні механізми. Точно так само справи йдуть і в мовах з класової моделлю, де, створюючи клас, ми відразу отримуємо механізм для створення об'єктів на його основі:

Найпростіший варіант - написати просту функцію, яка буде штампувати об'єкти з однаковими властивостями і методами. Такі об'єкти ніяк не будуть зв'язані між собою.

Інший варіант - написати особливу функцію, яка буде не тільки створювати об'єкти, але і зв'язувати їх між собою. Всім таким об'єктам будуть доступний один набір методів. Якщо згодом змінити його, наприклад, додати в нього новий метод, то цей метод з'явиться не тільки в майбутніх об'єктах, а й у всіх уже створених.

Давайте розглянемо обидва варіанти більш детально.

Створення об'єктів за допомогою простої функції

У найпершому прикладі ми створювали об'єкт myCar і додавали йому кілька методів. Помістимо цей код в функцію:

Тепер ми можемо використовувати цю функцію для штампування об'єктів:

Крім того, наші об'єкти ніяк не пов'язані між собою. Ця ситуація дуже нагадує реальні автомобілі з реального автозаводу: вони все виглядають однаково, але як тільки вони залишають конвеєр, вони повністю незалежні один від одного. Якщо в середині виробництва в моделі буде виявлений дефект, то для його усунення виробникові доведеться повертати всі автомобілі назад на автозавод.

Але у віртуальному світі нам нема чого обмежувати себе подібним чином. Ми можемо створювати об'єкти так, щоб мати можливість змінювати їх все одночасно.

Створення об'єктів за допомогою конструктора

Для початку повторимо рішення з простою функцією, яка штампує незалежні об'єкти, але вже у формі конструктора:

Тепер ми можемо викликати цю функцію за допомогою оператора new. а саме:

Оператор new створює новий об'єкт і викликає функцію-конструктор як метод цього об'єкта (тобто з посиланням на об'єкт у змінній this), після чого повертає створений об'єкт. Спрощено роботу оператора new можна проілюструвати наступним псевдокодом:

Як я вже і сказав, наш конструктор повторює рішення з простою функцією. Нам не потрібно створювати об'єкти вручну, однак ми як і раніше не можемо змінити метод honk () одночасно на всіх створених об'єктах. Але ми вже підготували фундамент для цього. Всі об'єкти, створювані конструктором, отримують особливе властивість, що зв'язує їх з конструктором:

Всі створені об'єкти myCar пов'язані з конструктором Car. Саме це відрізняє їх від випадкового набору об'єктів з однаковими методами і схожими іменами.

Тепер прийшов час поговорити про прототипи, про які я згадував на початку статті.

Використання прототипів для створення об'єктів із загальним функціоналом

Як я вже писав, в прототипної моделі програмування методи, загальні для всіх об'єктів, розміщуються в об'єкті-прототипі. Але де ж знаходиться об'єкт-прототип для наших об'єктів myCar. адже ми його не створювали? Він неявно створюється за нас і записується в властивість Car.prototype.

Ми також можемо замінити вже існуючий метод на новий:

Ми навіть можемо змінити метод тільки одного з наших об'єктів:

В останньому прикладі важливо розуміти, що відбувається за лаштунками. Як ми вже знаємо, під час виклику методу інтерпретатор проходить певний шлях для того щоб виконати цей метод. У об'єкта myCar1 як і раніше немає власного визначення методу honk () і інтерпретатор виконує метод об'єкта-прототипу. Але у випадку з myCar2 все інакше, у нього тепер є власний метод honk (). тому інтерпретатор більше не звертається за цим методом до прототипу.

Зверніть увагу, наскільки ефективніше використання прототипу в порівнянні зі створенням копій методів. Як уже згадувалося, інтерпретатора не зрозуміло, що ці методи повинні бути ідентичні, тому він виділяє пам'ять для кожного методу кожного створеного нами об'єкта. Але у випадку з прототипом все інакше - пам'ять виділяється тільки один раз незалежно від того, скільки об'єктів ми створимо.

Щоб не бути голослівним, наведу дуже просте порівняння. Перший приклад створює мільйон об'єктів і кожному з них додається копія одного методу:

У браузері Google Chrome виділяється область пам'яті розміром в 328 Мб. А ось цей же приклад, але метод перенесений в прототип:

На цей раз розмір області пам'яті всього 17 Мб, це приблизно 5% від попереднього значення.

спадкування

Безсумнівно, зручно мати клас об'єктів із загальними методами, але часто потрібно створити кілька різних класів об'єктів з деяким загальним функціоналом. Уявімо, що в нашому чудовому віртуальному світі є не тільки автомобілі, але і велосипеди. І ті, і інші вміють їздити, але замість клаксона у велосипеда дзвіночок.

Отже, метод drive () - загальний для обох класів, в той час як методи honk () і ring () відносяться кожен до свого класу. Виділимо клас Vehicle. від якого будуть успадковуватися класи Car і Bike.

ООП і спадкування в javascript - popel agency
Схема ієрархії класів

  • Інтерпретатор шукає метод drive () в самому об'єкті myCar. Цього методу в об'єкті не існує;
  • Інтерпретатор шукає метод drive () в прототипі конструктора Car. яким є об'єкт, створений за допомогою конструктора Vehicle. Тут методу також немає;
  • Інтерпретатор шукає метод drive () в прототипі конструктора Vehicle. де і знаходить шуканий метод.

Класи? Ні не чув. Частина 2

Давайте розберемося, що відбувається. Розберемо приклад:

Цей код дає практично такий же результат, як і створення конструкторів, але при цьому набагато коротше. За лаштунками метод Object.create () створює тимчасовий конструктор, як прототип якого використовується об'єкт, який передається нами. Після цього метод повертає нам об'єкт, створений за допомогою тимчасового конструктора.

Але стривайте-но, ми ж створили методи drive () і honk () прямо в самих об'єктах замість їх прототипів, хіба це не призведе до зростання споживання пам'яті? В даному випадку - ні. Ми створили чотири різні об'єкти, два з них - це об'єкти myCar. створені на основі об'єкта car. Але при цьому самі методи honk () і drive () визначено лише одного разу, а, отже, і в пам'яті вони існують лише одного разу. Проведемо експеримент:

З'ясовується, що розмір виділеної пам'яті таки збільшився - до 40 Мб. Але в обмін на спрощення коду це дозволена жертва.