Ответ 1
Оба образца кода, которые вы продемонстрировали в своем вопросе, используют прототипное наследование. На самом деле любой объектно-ориентированный код, который вы пишете в JavaScript, является парадигмой прототипного наследования. JavaScript просто не имеет классического наследования. Это должно немного прояснить ситуацию:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Как вы можете видеть, прототипное наследование и классическое наследование - это две разные парадигмы наследования. Некоторые языки, такие как Self, Lua и JavaScript, поддерживают прототипное наследование. Однако большинство языков, таких как С++, Java и С#, поддерживают классическое наследование.
Краткий обзор объектно-ориентированного программирования
Оба прототипального наследования и классического наследования являются объектно-ориентированными парадигмами программирования (т.е. они имеют дело с объектами). Объекты - это просто абстракции, которые инкапсулируют свойства объекта реального мира (т.е. Представляют собой реальные слова в программе). Это называется абстракцией.
Абстракция: Представление реальных вещей в компьютерных программах.
Теоретически абстракция определяется как "общая концепция, сформированная путем извлечения общих черт из конкретных примеров". Однако ради этого объяснения мы будем использовать вышеупомянутое определение.
Теперь некоторые объекты имеют много общего. Например, грязевой велосипед и Harley Davidson имеют много общего.
Велосипед грязи:
Харли Дэвидсон:
Бродяга и Харли Дэвидсон - оба байка. Следовательно, велосипед - это обобщение как мотоцикла грязи, так и Harley Davidson.
Bike
|
+---------------------------------+
| |
v v
Mud Bike Harley Davidson
В приведенном выше примере велосипед, грязевой велосипед и Harley Davidson - все абстракции. Однако велосипед - это более общая абстракция байдажа грязи и Harley Davidson (т.е. И грязевой велосипед, и Harley Davidson являются определенными типами велосипедов).
Обобщение: Абстракция более конкретной абстракции.
В объектно-ориентированном программировании мы создаем объекты (которые являются абстракциями объектов реального мира), и мы используем либо классы, либо прототипы для создания обобщений этих объектов. Обобщения создаются посредством наследования. Велосипед - это обобщение грязевого байка. Следовательно, грязевые велосипеды наследуются от велосипедов.
Классическое объектно-ориентированное программирование
В классическом объектно-ориентированном программировании мы имеем два типа абстракций: классы и объекты. Объект, как упоминалось ранее, является абстракцией объекта реального мира. Класс, с другой стороны, представляет собой абстракцию объекта или другого класса (т.е. Это обобщение). Например, рассмотрим:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | Man | Class of object johnDoe. |
| 3 | Human | Superclass of class Man. |
+----------------------+----------------+---------------------------------------+
Как вы можете видеть в классических объектно-ориентированных языках программирования, объекты - это только абстракции (т.е. все объекты имеют уровень абстракции 1), а классы - это только обобщения (т.е. все классы имеют уровень абстракции больше 1).
Объекты в классических объектно-ориентированных языках программирования могут быть созданы только посредством экземпляров классов:
class Human {
// ...
}
class Man extends Human {
// ...
}
Man johnDoe = new Man();
В суммировании в классических объектно-ориентированных языках программирования объекты представляют собой абстракции объектов реального мира, а классы - обобщения (т.е. абстракции любых объектов или других классов).
Следовательно, по мере того как уровень абстракции увеличивается, сущности становятся более общими, и по мере уменьшения уровня абстракции сущности становятся более конкретными. В этом смысле уровень абстракции аналогичен шкале от более конкретных объектов до более общих объектов.
Прототипное объектно-ориентированное программирование
Прототипные объектно-ориентированные языки программирования намного проще, чем классические объектно-ориентированные языки программирования, потому что в прототипном объектно-ориентированном программировании мы имеем только один тип абстракции (т.е. объекты). Например, рассмотрим:
+----------------------+----------------+---------------------------------------+
| Level of Abstraction | Name of Entity | Comments |
+----------------------+----------------+---------------------------------------+
| 0 | John Doe | Real World Entity. |
| 1 | johnDoe | Variable holding object. |
| 2 | man | Prototype of object johnDoe. |
| 3 | human | Prototype of object man. |
+----------------------+----------------+---------------------------------------+
Как вы можете видеть в прототипальных объектно-ориентированных языках программирования, объекты представляют собой абстракции любых объектов реального мира (в этом случае они просто называются объектами) или других объектов (в этом случае они называются прототипами тех объектов, которые они абстрактны), Следовательно, прототип является обобщением.
Объекты в прототипальных объектно-ориентированных языках программирования могут быть созданы либо ex-nihilo (т.е. из ничего), либо из другого объекта (который становится прототипом вновь созданного объекта):
var human = {};
var man = Object.create(human);
var johnDoe = Object.create(man);
По моему скромному мнению прототипные объектно-ориентированные языки программирования более мощные, чем классические языки объектно-ориентированного программирования, потому что:
- Существует только один тип абстракции.
- Обобщения - это просто объекты.
К настоящему времени вы, должно быть, осознали разницу между классическим наследованием и прототипным наследованием. Классическое наследование ограничено классами, наследуемыми от других классов. Однако прототипное наследование включает в себя не только прототипы, наследуемые от других прототипов, но и объекты, наследуемые от прототипов.
Изоморфизм прототипа класса
Вы, должно быть, заметили, что прототипы и классы очень похожи. Это правда. Они есть. На самом деле они настолько похожи, что вы действительно можете использовать прототипы для моделирования классов:
function CLASS(base, body) {
if (arguments.length < 2) body = base, base = Object.prototype;
var prototype = Object.create(base, {new: {value: create}});
return body.call(prototype, base), prototype;
function create() {
var self = Object.create(prototype);
return prototype.hasOwnProperty("constructor") &&
prototype.constructor.apply(self, arguments), self;
}
}
Используя вышеуказанную функцию CLASS
, вы можете создавать прототипы, которые выглядят как классы:
var Human = CLASS(function () {
var milliseconds = 1
, seconds = 1000 * milliseconds
, minutes = 60 * seconds
, hours = 60 * minutes
, days = 24 * hours
, years = 365.2425 * days;
this.constructor = function (name, sex, dob) {
this.name = name;
this.sex = sex;
this.dob = dob;
};
this.age = function () {
return Math.floor((new Date - this.dob) / years);
};
});
var Man = CLASS(Human, function (Human) {
this.constructor = function (name, dob) {
Human.constructor.call(this, name, "male", dob);
if (this.age() < 18) throw new Error(name + " is a boy, not a man!");
};
});
var johnDoe = Man.new("John Doe", new Date(1970, 0, 1));
Обратное неверно (т.е. вы не можете использовать классы для моделирования прототипов). Это связано с тем, что прототипы являются объектами, но классы не являются объектами. Это совершенно другой тип абстракции.
Заключение
В суммировании мы узнали, что абстракция является "общей концепцией, сформированной путем извлечения общих черт из конкретных примеров" и что обобщение является "абстракцией более конкретной абстракции". Мы также узнали о различиях между прототипным и классическим наследованием и о том, как обе они являются двумя гранями одной и той же монеты.
На прощальной заметке я хотел бы отметить, что существуют два шаблона прототипального наследования: прототипный шаблон и шаблон конструктора. Прототипная модель представляет собой канонический образец прототипного наследования, тогда как шаблон конструктора используется для того, чтобы сделать прототипное наследование более похожим на классическое наследование. Лично я предпочитаю прототипную схему.
P.S. Я тот парень, который написал сообщение в блоге " Почему вопрос о прототипном наследовании" и ответил на вопрос "Преимущества прототипного наследования по классическому?". Мой ответ - принятый ответ.