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

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

5 ответов


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

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

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

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

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

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

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

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

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

эта косвенность была предназначена для того, чтобы язык казался более знакомым классически обученным программистам, но не смогла этого сделать, так как мы можно видеть из очень низкого мнения 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 предоставляет лучший способ - делегация. Крокфорд!--99-->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

если вы заметили в вышеуказанной программе Боб Найстром отметить следующий анекдот в своем блоге о Pratt Parsers:

вы должны любить Java "пожалуйста, подпишите его в четырех экземплярах" уровень бюрократии здесь.

опять же, я думаю, это только потому, что Java так много сосет.

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

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

4. Прототипное наследование является динамическим

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

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

вывод

прототипное наследование имеет значение. Важно обучить программистов JavaScript тому, почему следует отказаться от шаблона конструктора прототипного наследования в пользу прототипная модель прототипного наследования.

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

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

Если вам понравился этот ответ, то вы также должны прочитать в моем блоге на "почему Прототипное Наследование Имеет Значение". Поверь мне, Ты не разочаруешься.


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

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

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

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

  1. проверка типа языка прототипа не невозможна, но это очень, очень сложно. Большинство" проверки типов " прототипических языков-это чистая утка времени выполнения ввод " - стиль проверки. Это не подходит для всех сред.
  2. аналогично трудно делать такие вещи, как оптимизация метода отправки статическим (или, часто, даже динамическим!) анализ. Это can (Я подчеркиваю: can) быть очень неэффективным очень легко.
  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 прототипное наследование