наследование на основе прототипов и классов - PullRequest
190 голосов
/ 03 мая 2009

В JavaScript каждый объект является одновременно экземпляром и классом. Для наследования вы можете использовать любой экземпляр объекта в качестве прототипа.

В Python, C ++ и т. Д. Существуют классы и экземпляры как отдельные понятия. Чтобы сделать наследование, вы должны использовать базовый класс для создания нового класса, который затем можно использовать для создания производных экземпляров.

Почему JavaScript пошел в этом направлении (ориентация объекта на основе прототипа)? Каковы преимущества (и недостатки) ОО на основе прототипа по сравнению с традиционным ОО на основе классов?

Ответы [ 3 ]

189 голосов
/ 03 мая 2009

Здесь есть около сотни терминологических проблем, в основном построенных вокруг кого-то (не вас), пытающегося сделать свою идею похожей на The Best.

Все объектно-ориентированные языки должны уметь работать с несколькими понятиями:

  1. инкапсуляция данных вместе со связанными с ними операциями над данными, по-разному известными как элементы данных и функции-члены, или как данные и методы, среди прочего.
  2. наследование, способность сказать, что эти объекты точно такие же, как и другой набор объектов, КРОМЕ этих изменений
  3. полиморфизм («много форм»), в котором объект сам решает, какие методы следует запустить, чтобы вы могли зависеть от языка для правильной маршрутизации ваших запросов.

Теперь, что касается сравнения:

Во-первых, это вопрос "класса" против "прототипа". Идея изначально возникла в Simula, где с помощью метода на основе классов каждый класс представлял набор объектов, которые разделяли одно и то же пространство состояний (читайте «возможные значения») и одни и те же операции, формируя таким образом класс эквивалентности. Если вы посмотрите на Smalltalk, так как вы можете открыть класс и добавить методы, это практически то же самое, что вы можете сделать в Javascript.

Позже ОО-языки хотели использовать статическую проверку типов, поэтому мы получили понятие фиксированного класса, установленного во время компиляции. В открытой версии у вас было больше гибкости; в более новой версии у вас была возможность проверить некоторые виды корректности в компиляторе, которые в противном случае потребовали бы тестирования.

На языке «классов» это копирование происходит во время компиляции. В языке прототипа операции хранятся в структуре данных прототипа, которая копируется и изменяется во время выполнения. Однако, абстрактно, класс по-прежнему является классом эквивалентности всех объектов, которые используют одно и то же пространство состояний и методы. Когда вы добавляете метод к прототипу, вы фактически создаете элемент нового класса эквивалентности.

Теперь, зачем это? прежде всего потому, что он обеспечивает простой, логичный, элегантный механизм во время выполнения. Теперь, чтобы создать новый объект, или , чтобы создать новый класс, вам просто нужно выполнить глубокое копирование, копируя все данные и структуру данных прототипа. Вы получаете наследование и полиморфизм более или менее бесплатно: поиск метода всегда состоит из запроса словаря для реализации метода по имени.

Причина, по которой появился сценарий Javascript / ECMA, заключается в том, что, когда мы начинали с этим 10 лет назад, мы имели дело с гораздо менее мощными компьютерами и гораздо менее сложными браузерами. Выбор метода на основе прототипа означал, что интерпретатор может быть очень простым при сохранении желаемых свойств ориентации объекта.

37 голосов
/ 13 декабря 2010

Сравнение, слегка смещенное в сторону подхода на основе прототипов, можно найти в статье - Самость: сила простоты . В статье приводятся следующие аргументы в пользу прототипов:

Создание путем копирования . Создание новых объектов из прототипов осуществляется путем простая операция, копирование, с простой биологической метафорой, клонирование. Создание новые объекты из классов осуществляется путем создания экземпляров, который включает в себя интерпретация формата информации в классе. Инстанциация похоже на строительство дома из плана. Копирование обращений к нам в качестве более простой метафоры чем экземпляр.

Примеры существующих модулей . Прототипы конкретнее классов потому что они являются примерами объектов, а не описаниями формата и инициализации. Эти примеры могут помочь пользователям повторно использовать модули, упрощая их Понимаю. Система на основе прототипа позволяет пользователю изучить типичного представителя вместо того, чтобы требовать, чтобы он имел смысл из его описания.

Поддержка уникальных объектов . Self обеспечивает структуру, которая может легко включать уникальные объекты со своим поведением. Поскольку каждый объект имеет именованные слоты и слоты могут содержать состояние или поведение, любой объект может иметь уникальные слоты или поведение. Системы на основе классов предназначены для ситуаций, когда Есть много объектов с одинаковым поведением. Нет лингвистической поддержки для объект обладает своим собственным уникальным поведением, и неудобно ( думать, что шаблон Singleton ) создать класс, который гарантированно будет иметь только один экземпляр. Самость не страдает ни от одного из этих недостатков. Любой объект может быть настроен с его собственным поведением. Уникальный объект может удерживать уникальное поведение, и отдельный «экземпляр» не нужен.

Исключение метарегрессии . Ни один объект в системе на основе классов не может быть самодостаточным; другой объект (его класс) необходим для выражения его структуры и поведения. Это приводит к концептуально бесконечной метарегрессии: point является экземпляром класса Point, который является экземпляром метакласса Point, который является экземпляром метакласса Point, до бесконечности. С другой стороны, в системах на основе прототипов объект может включать собственное поведение; никакой другой объект не нужен, чтобы вдохнуть в него жизнь. Прототипы устранить метарегрессию.

Self , вероятно, является первым языком для реализации прототипов. (Он также впервые применил другие интересные технологии, такие как JIT, которая позже появилась в JVM. Поэтому чтение других статей Self также должно быть поучительным).

22 голосов
/ 03 мая 2009

Вы должны проверить великую книгу по JavaScript Дуглас Крокфорд . Это очень хорошее объяснение некоторых дизайнерских решений, принятых создателями JavaScript.

Одним из важных аспектов проектирования JavaScript является его система наследования прототипов. Объекты являются гражданами первого класса в JavaScript, так что обычные функции также реализованы как объекты (точнее, объект «Функция»). По моему мнению, когда он изначально был разработан для запуска внутри браузера, он должен был использоваться для создания множества одноэлементных объектов. В браузере DOM вы найдете это окно, документ и т. Д. Все одноэлементные объекты. Кроме того, JavaScript является свободно типизированным динамическим языком (в отличие от, скажем, Python, который строго типизирован, динамический язык), в результате концепция расширения объекта была реализована с использованием свойства «prototype».

Так что я думаю, что есть некоторые плюсы для ОО на основе прототипов, реализованных в JavaScript:

  1. Подходит для свободно типизированных сред, нет необходимости определять явные типы.
  2. Упрощает реализацию одноэлементного паттерна (сравните JavaScript и Java в этом отношении, и вы поймете, о чем я говорю).
  3. Предоставляет способы применения метода объекта в контексте другого объекта, динамического добавления и замены методов из объекта и т. Д. (То, что невозможно в строго типизированных языках).

Вот некоторые минусы прототипа OO:

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