Прототип, заснований на класах, спадкування


208

У JavaScript кожен об'єкт одночасно є екземпляром та класом. Щоб зробити спадщину, ви можете використовувати будь-який екземпляр об'єкта як прототип.

У Python, C ++ та ін. Є класи та екземпляри як окремі поняття. Щоб зробити спадщину, вам потрібно використовувати базовий клас для створення нового класу, який потім може бути використаний для отримання похідних екземплярів.

Чому JavaScript пішов у цьому напрямку (орієнтована на прототип орієнтація на об'єкти)? які переваги (і недоліки) ОО на основі прототипу щодо традиційних ОО на основі класів?


10
На JavaScript впливав Self, який був першою мовою з успадкуванням прототипів. У той час класичне успадкування було все лють, вперше запроваджене в Симулі. Однак класичне успадкування було надто складним. Тоді у Девіда Унгара та Рендала Сміта було прочитано епіфанію після читання GEB - "Найбільш конкретна подія може слугувати загальним прикладом класу подій". Вони зрозуміли, що для об'єктно-орієнтованого програмування класи не потрібні. Звідси народився Я. Щоб дізнатися , як прототіпічного спадок краще , ніж класичне спадкування прочитати: stackoverflow.com/a/16872315/783743 =)
Aadit M Shah

@AaditMShah Що / хто є GEB?
Алекс

3
@ Алекс ГЕБ - книга, написана Дугласом Хофстадтером. Це абревіатура Геделя Ешера Баха. Курт Гедель був математиком. Ешер був художником. Бах був піаністом.
Аадіт М Шах

Відповіді:


201

Тут існує близько сотні термінологічних питань, здебільшого побудованих навколо когось (не ви), який намагається зробити їхню ідею схожою на The Best.

Усі об'єктно-орієнтовані мови повинні вміти мати декілька понять:

  1. інкапсуляція даних разом із супутніми операціями над даними, широко відомими як члени даних та функції членів, або як дані та методи, серед іншого.
  2. успадкування, здатність сказати, що ці об'єкти так само, як і інший набір об'єктів ВСЕ, крім цих змін
  3. поліморфізм ("багато фігур"), в якому об'єкт вирішує сам, які методи слід запустити, щоб ви могли залежати від мови, щоб правильно направляти ваші запити.

Тепер, що стосується порівняння:

Перше - це питання "класу" проти "прототипу". Спочатку ідея почалася в Simula, де методом на основі класу кожен клас представляв собою набір об'єктів, які поділяють один і той же простір стану (читати "можливі значення") і ті ж операції, утворюючи тим самим клас еквівалентності. Якщо ви озираєтесь на Smalltalk, оскільки ви можете відкрити клас та додати методи, це фактично те саме, що ви можете зробити в Javascript.

Пізніше мови OO хотіли використовувати статичну перевірку типу, тому ми отримали поняття фіксованого класу, встановленого під час компіляції. У версії відкритого класу ви мали більше гнучкості; у новій версії ви мали можливість перевірити деякі види правильності у компіляторі, які в іншому випадку потребували б тестування.

Мовою "на основі класу" таке копіювання відбувається під час компіляції. Мовою прототипу операції зберігаються у структурі даних прототипу, яка копіюється та змінюється під час виконання. Однак, абстрактно, клас - це все-таки клас еквівалентності всіх об'єктів, які мають однаковий простір станів і методів. Додаючи метод до прототипу, ви ефективно створюєте елемент нового класу еквівалентності.

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

Причина, що опинилася в сценарії Javascript / ECMA, полягає в тому, що коли ми починали це 10 років тому, ми мали справу з набагато менш потужними комп'ютерами та набагато менш складними браузерами. Вибір методу, заснованого на прототипі, означав, що інтерпретатор може бути дуже простим, зберігаючи бажані властивості орієнтації об'єкта.


1
Правильно, чи читається цей парагаф, ніби я маю на увазі інше? Даль і Найквіст придумали "клас" як колекцію речей з однаковим підписом методу.
Чарлі Мартін

1
Це зміна говорить про це краще?
Чарлі Мартін

2
Ні, вибачте, CLOS походить з кінця 80-х років dreamongs.com/CLOS.html Smalltalk з 1980 року en.wikipedia.org/wiki/Smalltalk та Simula з повною орієнтацією на об'єкт з 1967-68 en.wikipedia.org/wiki/Simula
Чарлі Мартін

3
@Stephano, Вони не настільки чіткі, як усе, що Python, Ruby, Smalltalk використовують словники для пошуку методу, а javascript та Self мають класи. Певною мірою можна стверджувати, що різниця полягає лише в тому, що мови, орієнтовані на прототип, піддають їх реалізації. Тому, мабуть, добре не перетворювати це на велику угоду: це, мабуть, більше схоже на аргумент між EMACS та vi.
Чарлі Мартін

21
Корисна відповідь . +1 Менше корисних мотлохів у коментарях. Я маю на увазі, чи має значення це те, чи був CLOS або Smalltalk першим? Більшість людей тут все одно не історики.
Адам Арольд

40

Порівняння, дещо упереджене до підходу, заснованого на прототипах, можна знайти у статті Self: The Power of Simplicity . У роботі викладаються такі аргументи на користь прототипів:

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

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

Підтримка унікальних об'єктів . Self пропонує рамки, які можуть легко включати в себе унікальні об'єкти з власною поведінкою. Оскільки кожен об'єкт має назву слотів, і слоти можуть містити стан або поведінку, будь-який об'єкт може мати унікальні слоти або поведінку. Системи на основі класів розроблені для ситуацій, коли є багато об’єктів з однаковою поведінкою. Немає лінгвістичної підтримки об’єкта, який має власну унікальну поведінку, і незручно створювати клас, який гарантовано матиме лише один екземпляр [ думаю, однотонний шаблон ]. Я не страждає ні від одного з цих недоліків. Будь-який об’єкт може бути налаштований під власну поведінку. Унікальний об'єкт може містити унікальну поведінку, а окремий "екземпляр" не потрібен.

Усунення мета-регресу . Жоден об'єкт в системі, що базується на класах, не може бути самодостатнім; інший об’єкт (його клас) необхідний для вираження його структури та поведінки. Це призводить до концептуально нескінченного мета-регресу: a point- це екземпляр класу Point, який є екземпляром метакласу Point, який є екземпляром метаметакласу Point, ad infinitum. З іншого боку, в системах, заснованих на прототипі, об'єкт може включати власну поведінку; жоден інший об’єкт не потрібен, щоб вдихнути в нього життя. Прототипи усувають мета-регрес.

Self - це, мабуть, перша мова, яка впровадила прототипи (вона також стала першою іншою цікавою технологією на зразок JIT, яка пізніше пробилася в JVM), тому читайте інших самодокументів також повинно бути повчальним.


5
RE: Усунення мета-регресу: У загальній об'єктній системі Lisp, яка заснована на класах, a point- це екземпляр класу Point, який є екземпляром метакласу standard-class, який є самим екземпляром, ad finitum.
Макс Нанасі

Посилання на власні папери мертві. Робочі посилання: Я: Сила простоти | Самостійна бібліографія
користувач1201917

24

Ви повинні перевірити чудову книгу на JavaScript від Дугласі Crockford . Це дає дуже хороше пояснення деяких дизайнерських рішень, прийнятих творцями JavaScript.

Одним з важливих аспектів дизайну JavaScript є його прототипічна система успадкування. Об'єкти є громадянами першого класу в JavaScript, настільки, що регулярні функції також реалізуються як об'єкти ("Функція", щоб бути точним). На мою думку, коли він спочатку був розроблений для роботи в браузері, його мали використовувати для створення безлічі однотонних об'єктів. У DOM веб-переглядача ви знайдете це вікно, документ тощо. Крім того, JavaScript є слабко набраною динамічною мовою (на відміну від сказаного Python, яка сильно набрана, динамічна мова), в результаті концепція розширення об'єкта була реалізована за допомогою властивості 'прототип'.

Тож я думаю, що існують деякі плюси для прототипу OO, який реалізований у JavaScript:

  1. Підходить у слабко типових середовищах, не потрібно визначати явні типи.
  2. Це робить неймовірно простим втілення одиночного шаблону (порівняйте JavaScript і Java в цьому плані, і ви будете знати, про що я говорю).
  3. Надає способи застосування методу об'єкта в контексті іншого об'єкта, додавання та заміна методів динамічно з об'єкта тощо (речі, які неможливо на мовах із сильним типом).

Ось деякі з мінусів прототипічного OO:

  1. Непростий спосіб реалізації приватних змінних. Можливо реалізувати приватні варіанти, використовуючи майстерство Крокфорда , використовуючи закриття , але це, безумовно, не так банально, як використання приватних змінних, скажімо, у Java або C #.
  2. Я ще не знаю, як реалізувати декілька спадків (наскільки це варто) в JavaScript.

2
Просто використовуйте умову іменування для приватних vars, як це робить Python.
aehlke

1
у js спосіб робити приватні варіанти - із закриттям, і це не залежить від обраного вами типу успадкування.
Бенджа

6
Крокфорд багато зробив, щоб пошкодити JavaScript, завдяки тому, що досить проста мова сценаріїв перетворилася на загадкове захоплення її внутрішніми. JS не має справжнього приватного діапазону ключових слів або справжнього множинного успадкування: не намагайтеся підробити їх.
Hal50000
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.