Примером прототипического наследования, которое не включает моделирование классического наследования? - PullRequest
6 голосов
/ 01 апреля 2011

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

Хороший пример наследования JavaScript на основе прототипов

наследование JavaScript

Использование шаблонов наследования в JavaScript

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

Ответы [ 4 ]

6 голосов
/ 01 апреля 2011

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

Одним из преимуществ наследования прототипов в JavaScript является возможность динамического добавления во время выполнения новых методов или изменения старых, доступных длявсе объекты (без добавления потери памяти для каждого объекта).

Это может быть опасно, особенно когда встроенные методы, такие как String или Object, имеют свои встроенные методы, которые переопределяются в обратном (или угрожающем) стиле.

String.prototype.replace = function () {
    return 'hahaha';
};

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

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

И поведение прототипа даже вступает в игру даже внутри «класса», так как вы можете воспользоваться удобством хранения непосредственно на объекте (хотя в таких случаях он добавляет памяти, и, вероятно, лучше просто создать новый класс - ноэто все еще может быть удобно).

function Dog (type) {
    if (type === 'poodle') {
        this.bark = function () {
            alert('(yapyapyap)');
        };
    }
}
Dog.prototype.bark = function () {
    alert('(woof)');
};

var muffy = new Dog('poodle');
muffy.bark(); // '(yapyapyap)'
var rover = new Dog();
rover.bark(); // '(woof)'

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

function Creature () {}
Creature.prototype.respire = function () { return 'oooooh'; };

function createClass (o, f) {
    f = f || function f () {}
    f.prototype = (typeof o === 'function') ? o.prototype : o.constructor.prototype;
    f.prototype.constructor = f;
    return f;
}

var animals = ['Dog', 'Tiger', 'Lion', 'Frog', 'Kangaroo'];
animals.forEach(function (animal) {
    window[animal] = createClass(Creature);
});
var rover = new Dog();

Наконец, вы можете избежать строгой иерархии is-a, заимствуя только то, что вам нужно, и в то же время пользуясь наследуемыми функциями:

function createMixinClass (old, constructor, newMethods) {
    if (typeof constructor === 'object') {
        newMethods = constructor;
        constructor = null;
    }
    var proto = old.prototype, constructor = constructor || function () {};

    for (var m in proto) {
        constructor.prototype[m] = proto[m];
    }
    for (var method in newMethods) {
        if (!newMethods[method]) {
            delete constructor.prototype[method];
        }
        else {
            constructor.prototype[method] = newMethods[method];
        }
    }
    return constructor;
}

var Cat = createMixinClass(Dog, {bark:null, meow: function () {alert('meow');}});
var kitty = new Cat();

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

2 голосов
/ 02 августа 2013

Существует множество примеров наследования прототипов в дикой природе. Примечательно, что JQuery использует его. Каждый раз, когда вы делаете выбор jQuery, вы используете прототип делегата для наследования методов jQuery. Он также широко используется в различных веб-приложениях, включая платформу Creative Cloud от Abode, многие продукты от Yahoo и т. Д. *

Фактически, каждая реализация классического наследования в JavaScript фактически использует наследование прототипа для имитации классического наследования - необходимо только для удобства программистов, которые больше знакомы с классическим, чем с наследованием прототипов. Наследование прототипа настолько гибко, что легко имитировать особенности классического наследования с использованием наследования прототипа. Обратное неверно.

Прототипное наследование означает просто, что объект наследуется непосредственно от другого объекта. Вот пример:

var switchProto = {
  isOn: function isOn() {
    return this.state;
  },

  toggle: function toggle() {
    this.state = !this.state;
    return this;
  },

  state: false
},
switch1 = Object.create(switchProto),
switch2 = Object.create(switchProto);

Обычно этот вызов Object.create() помещают в фабричную функцию, чтобы сделать создание объекта более удобным.

Существует множество проблем с классическим наследованием, которых не существует с наследованием прототипов, таких как:

Классическое наследство

Плотная муфта . Наследование - самая прочная связь, доступная в дизайне OO. У классов потомков есть глубокие знания классов их предков.

Негибкие иерархии (дублирование по необходимости) . Иерархии с одним родителем редко способны описать все возможные варианты использования. В конце концов, все иерархии «неправильны» для нового использования - проблема, которая требует дублирования кода.

Многократное наследование сложно . Часто желательно наследовать от более чем одного родителя. Этот процесс чрезмерно сложен, и его реализация несовместима с процессом одиночного наследования, что затрудняет его чтение и понимание.

Хрупкая архитектура . Из-за тесной связи часто бывает трудно реорганизовать класс с «неправильным» дизайном, потому что большая часть существующих функций зависит от существующего дизайна.

Проблема Гориллы / Банана . Часто есть части родительского объекта, которые вы не хотите наследовать. Подклассы позволяют переопределять свойства родительского объекта, но не позволяют выбирать, какие свойства вы хотите наследовать.

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

Чтобы понять, как наследование прототипа решает эти проблемы, вы должны сначала понять, что существует два разных типа наследования прототипа. JavaScript поддерживает оба:

Делегирование . Если свойство не найдено в экземпляре, оно ищется в прототипе экземпляра. Это позволяет вам совместно использовать методы среди многих экземпляров, предоставляя вам шаблон flyweight бесплатно .

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

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

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

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

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

Плоские иерархии . С прототипом ОО тривиально сохранять иерархии наследования плоскими - используя конкатенацию и делегирование, вы можете иметь один уровень делегирования объекта и один экземпляр без ссылок на родительские классы.

Тривиальное множественное наследование . Наследование от нескольких предков так же просто, как объединение свойств из нескольких прототипов с использованием конкатенации для формирования нового объекта или нового делегата для нового объекта.

Гибкая архитектура . Поскольку вы можете выборочно наследовать с помощью прототипа OO, вам не нужно беспокоиться о проблеме «неправильного дизайна». Новый класс может наследовать любую комбинацию свойств от любой комбинации исходных объектов. Из-за простоты выравнивания иерархии изменение в одном месте не обязательно вызывает рябь в длинной цепочке объектов-потомков.

Горилл больше нет . Выборочное наследование устраняет проблему бананов горилл.

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

2 голосов
/ 05 апреля 2011

Это не «обучающий» пример, а «использование в реальном мире» прототипного наследования - это плагины jQuery.$.fn на самом деле является прототипом волшебной коллекции jQuery, и подключаемые модули jQuery добавляют методы для добавления функциональности в любую коллекцию jQuery.

1 голос
/ 01 апреля 2011

На Self Language Blog есть несколько интересных ссылок, включая некоторые видео.

...