Основанное на прототипе и наследование на основе класса

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

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

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

Ответ 1

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

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

  • инкапсуляция данных вместе со связанными операциями над данными, известными как члены данных и функции-члены, а также, в частности, как данные и методы.
  • наследование, способность сказать, что эти объекты подобны этому другому набору объектов EXCEPT для этих изменений
  • полиморфизм ( "много форм" ), в котором объект решает для себя, какие методы должны выполняться, чтобы вы могли правильно зависеть от языка для правильного направления ваших запросов.

Теперь, если сравнивать:

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

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

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

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

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

Ответ 2

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

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

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

Поддержка уникальных объектов. Self предоставляет структуру, которая может легко включать в себя уникальные объекты со своим поведением. Поскольку каждый объект имеет именованные слоты, а слоты могут содержать состояние или Поведение, любой объект может иметь уникальные слоты или поведение. Класс на основе системы предназначены для ситуаций, когда есть много объектов с такое же поведение. Нет лингвистической поддержки для объекта обладают своим собственным уникальным поведением, и неудобно создавать класс, который гарантированно имеет только один экземпляр [думаю, синглтон шаблон]. Самость не страдает ни от одного из этих недостатков. Любой объект можно настроить с его собственным поведением. Уникальный объект может содержать уникальное поведение, и отдельный "экземпляр" не требуется.

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

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

Ответ 3

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

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

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

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

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

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