Преимущества прототипного наследования над классическим?


Так что я, наконец, перестал волочить ноги все эти годы и решил изучать JavaScript "правильно". Один из самых головокружительных элементов дизайна языков-это реализация наследования. Имея опыт работы в Ruby, я был очень рад видеть закрытие и динамическую типизацию; но за всю свою жизнь я не могу понять, какие преимущества должны быть получены от экземпляров объектов, использующих другие экземпляры для наследования.

5 247

5 ответов:

я знаю, что этот ответ опоздал на 3 года, но я действительно думаю, что текущие ответы не дают достаточной информации о насколько прототипное наследование лучше классического наследования.

сначала давайте рассмотрим наиболее распространенные аргументы JavaScript-программистов в защиту прототипного наследования (я беру эти аргументы из текущего пула ответов):

  1. все просто.
  2. это мощно.
  3. It приводит к меньшему, менее избыточному коду.
  4. это динамический и, следовательно, это лучше для динамических языков.

теперь эти аргументы все действительны, но никто не потрудился объяснить, почему. Это все равно что сказать ребенку, что изучение математики важно. Конечно, это так, но ребенку, конечно, все равно; и вы не можете сделать ребенка похожим на математику, сказав, что это важно.

я думаю, что проблема с прототипным наследованием, что это объясняется перспектива JavaScript. Я люблю JavaScript, но прототипное наследование в JavaScript неверно. В отличие от классического наследования существуют две модели прототипического наследования:

  1. прототипный паттерн прототипного наследования.
  2. шаблон конструктора прототипного наследования.

к сожалению JavaScript использует шаблон конструктора прототипного наследования. Это потому, что, когда JavaScript был создан, Брендан Эйх (создатель JS) хотел, чтобы он выглядел как Java (который имеет классическое наследование):

и мы подталкивали его как младшего брата к Java, поскольку дополнительный язык, такой как Visual Basic, был C++ в семействах языков Microsoft в то время.

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

люди из таких языков, как Java, которые имеют классическое наследование, еще больше запутываются, потому что, хотя конструкторы выглядят как классы, они не ведут себя как классы. Как Дуглас Крокфорд указано:

эта косвенность была предназначена для того, чтобы язык казался более знакомым классически подготовленным программистам, но не смог этого сделать, поскольку мы можно видеть из очень низкого мнения Java-программистов о JavaScript. Шаблон конструктора JavaScript не привлекал классическую толпу. Это также затмило истинную прототипическую природу JavaScript. В результате, существует очень мало программистов, которые знают, как эффективно использовать язык.

вот оно. Прямо из пасти лошади.

Истинное Прототипное Наследование

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

  1. создать новый объект.
  2. клонировать существующий объект и расширить его.

Примечание: JavaScript предлагает два способа клонирования объекта -делегация и объединение. Отныне я буду использовать слово "клон" исключительно для обозначения наследования через делегирование, и слово "копировать" относится исключительно к наследованию через конкатенацию.

Хватит говорить. Давайте рассмотрим несколько примеров. Скажем, у меня есть круг радиуса 5:

var circle = {
    radius: 5
};

мы можем вычислить площадь и длину окружности круга от его радиуса:

circle.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};

circle.circumference = function () {
    return 2 * Math.PI * this.radius;
};

теперь я хочу создать еще один круг радиусом 10. Один из способов сделать это будет:

var circle2 = {
    radius: 10,
    area: circle.area,
    circumference: circle.circumference
};

однако JavaScript предоставляет лучший способ - делегация. Крокфорд по Object.create функция используется для этого:

var circle2 = Object.create(circle);
circle2.radius = 10;

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

вы можете спросить: "как это просто? Каждый раз, когда я хочу создать новый круг, мне нужно клонировать circle и вручную назначить ему радиус". Ну решение состоит в том, чтобы использовать функцию, чтобы сделать тяжелая работа для вас:
function createCircle(radius) {
    var newCircle = Object.create(circle);
    newCircle.radius = radius;
    return newCircle;
}

var circle2 = createCircle(10);

на самом деле вы можете объединить все это в один литерал объекта следующим образом:

var circle = {
    radius: 5,
    create: function (radius) {
        var circle = Object.create(this);
        circle.radius = radius;
        return circle;
    },
    area: function () {
        var radius = this.radius;
        return Math.PI * radius * radius;
    },
    circumference: function () {
        return 2 * Math.PI * this.radius;
    }
};

var circle2 = circle.create(10);

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

если вы заметили в приведенной выше программе

позвольте мне на самом деле ответить на вопрос inline.

прототип наследования имеет следующие достоинства:

  1. Он лучше подходит для динамических языков, потому что наследование так же динамично, как и среда, в которой оно находится. (Применимость к JavaScript должна быть очевидна здесь.) Это позволяет вам делать вещи быстро на лету, как настройка классов без огромного количества кода инфраструктуры.
  2. проще реализовать прототипирование объектная схема, чем классические схемы дихотомии класса / объекта.
  3. это устраняет необходимость в сложных острых краях вокруг объектной модели, таких как" метаклассы " (мне никогда не нравился метакласс... прости!) или "собственные значения" или тому подобное.

однако он имеет следующие недостатки:

  1. проверка типа языка прототипа не является невозможным, но это очень, очень трудно. Большинство "проверки типа" прототипических языков-это чистая утка времени выполнения ввод " - стиль проверки. Это не подходит для всех сред.
  2. аналогично трудно делать такие вещи, как оптимизация метода отправки статическим (или, часто, даже динамическим!) анализ. Это можете (Я подчеркиваю: можете) быть очень неэффективным очень легко.
  3. аналогично создание объекта может быть (и обычно является) намного медленнее в языке прототипирования, чем в более обычной схеме дихотомии класса/объекта.

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

IMO основным преимуществом прототипного наследования является его простота.

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

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

Если вы думаете прообразовательно вы скоро заметите, что вам не нужны классы...

прототипное наследование будет гораздо более популярным в ближайшем будущем,ECMAScript 5th Edition спецификация представил Object.create метод, который позволяет создать новый экземпляр объекта, который наследует от другого очень простым способом:

var obj = Object.create(baseInstance);

эта новая версия стандарта реализовано всеми поставщиками браузеров, и я думаю, что мы начнем видеть более чистое прототипное наследование...

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

первообразного

var single = { status: "Single" },
    princeWilliam = Object.create(single),
    cliffRichard = Object.create(single);

console.log(Object.keys(princeWilliam).length); // 0
console.log(Object.keys(cliffRichard).length); // 0

// Marriage event occurs
princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1 (New instance property)
console.log(Object.keys(cliffRichard).length); // 0 (Still refers to prototype)

классический с методами экземпляра (неэффективно, потому что каждый экземпляр хранит свое собственное свойство)

function Single() {
    this.status = "Single";
}

var princeWilliam = new Single(),
    cliffRichard = new Single();

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 1

эффективная классическая

function Single() {
}

Single.prototype.status = "Single";

var princeWilliam = new Single(),
    cliffRichard = new Single();

princeWilliam.status = "Married";

console.log(Object.keys(princeWilliam).length); // 1
console.log(Object.keys(cliffRichard).length); // 0
console.log(cliffRichard.status); // "Single"

Веб-разработка: прототипное наследование против классического наследования

http://chamnapchhorn.blogspot.com/2009/05/prototypal-inheritance-vs-classical.html

классическое Vs прототипное наследование-переполнение стека

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