Прототипна об'єктно-орієнтоване програмування в javascript, javascript

прототипна ООП

В клас-орієнтованому програмуванні ми спочатку створюємо клас як шаблон, а потім на його основі створюємо об'єкт. Для побудови більш конкретних типів об'єктів ми створюємо нащадка класу. Тобто, ми вносимо деякі зміни в шаблон і використовуємо отриманий новий шаблон для побудови більш конкретних об'єктів.

У реальному світі, якщо потрібно створити стілець, ви спочатку створили б проект на папері, а потім виготовили стільці на основі цього проекту. Проект тут - клас, а стільці є об'єктами. Якби ви захотіли зробити крісло-гойдалку, ви б взяли проект, внесли деякі зміни і створили б крісло-гойдалку.

Object.create (null) створює новий порожній об'єкт. Далі, ми додамо властивості і функції нашого нового об'єкту:

genericAnimal - об'єкт і відповідно може бути використаний:

Ми тільки що створили кішку, як клону звичайного тваринного. Ми можемо додати їй властивості і функції:

Також ми можемо використовувати нашу кішку в якості прототипу і створити ще кілька кішок:

Ключове слово New і функція-конструктор

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

Розуміння делегування і реалізація прототипів

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

Деякі приклади з попереднього коду можуть проілюструвати цей момент:

Наступний рядок створює новий порожній об'єкт з нульовим __proto__:

Наступний рядок створить порожній об'єкт з __proto__. що вказує на rodent:

Як бачите, кожен об'єкт містить посилання на свій прототип. Розглянемо функцію Object.create. Здається, що функція "клонує" батьківський об'єкт, а властивості батька копіюються в дочірній елемент, але це не так. Коли capybara створюється з rodent. capybara - це порожній об'єкт з посиланням на rodent.

Але тоді, якщо ми викличемо capybara.size відразу після створення, ми отримаємо S розмір, який встановлений в батьківському об'єкті. Що ж це за магія така? Адже capybara ще не має властивості розміру. Проте, коли ми пишемо capybara.size. ми так чи інакше можемо отримати властивість size прототипу.

Завдання властивості трохи відрізняється. Коли ми встановлюємо capybara.size = 'XXL'. нове властивостей size. створюється в capybara об'єкті. І наступного разу, коли ми спробуємо отримати доступ до capybara.size. ми знайдемо його безпосередньо в об'єкті зі значенням 'XXL'.

Оскільки властивість прототипу є посиланням, зміна властивостей об'єкта прототипу вплине на всі об'єкти, які використовують прототип. Наприклад, якщо після створення rodent і capybara ми перепишемо функцію description або додамо нову функцію в genericAnimal. вони негайно стануть доступні для використання в rodent і capybara завдяки делегуванню.

створення Object.create

Object.create в дії

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

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

ООП на основі класів в порівнянні з прототипна ООП

Обидва підходи мають свої плюси і мінуси. Але прототипна ООП простіше для розуміння, воно більш гнучке і динамічний.

Щоб отримати уявлення про його динамічному характері, погляньте на наступний приклад: ви написали код, який використовує indexOf функцію в масивах. Після його написання і тестування в хорошому браузері ви неохоче перевіряєте його в Internet Explorer 8. Як і слід було очікувати, ви стикаєтеся з проблемами. Так як функція indexOf не визначена в IE8.

Так що ж робити? В ООП на основі класів можна вирішити цю проблему, визначивши функцію, в іншому класі "помічника", який приймає Array або List в якості вхідних даних, і замінює всі виклики в вашому коді. Або можна розділити List або ArrayList на підкласи, визначити функцію в підкласі і використовувати новий підклас замість ArrayList.

Емуляція ООП на основі класів, що може піти не так

Розглянемо наступний приклад, написаний з псевдоклас:

Це зразок наслідування. Проте, дещо кумедне тут відбувається - якщо ви перевірите colonel.offspring і puff.offspring. то помітите, що кожен з них містить два однакових нащадка!

Оскільки це виглядає як просте ООП на основі класу, ми можемо використовувати цю функцію для побудови ієрархії успадкування, як показано в наступному прикладі:

Все це виглядає добре, поки ви думаєте класовим ООП. Але якщо ви спробуєте table.events [ 'click .expand'] в консолі, то побачите "expand"! Так чи інакше, у HideableTableView є обробник події expand. хоча він не був визначений в цьому класі.

Побачити цю проблему в дії можна тут.

Представлена ​​вище проблема сталася з тієї ж причини, описаної в попередньому прикладі. У Backbone.js потрібно працювати, намагаючись зробити його схожим на класи, щоб побачити ланцюжок прототипів, приховану в фоновому режимі. Як тільки зрозумієте, як структурована ланцюжок прототипів, ви зможете знайти просте рішення цієї проблеми.

висновок

Схожі статті