Обидва зразки коду, які ви продемонстрували у своєму запитанні, використовують успадкування прототипів. Насправді будь-який об'єктно-орієнтований код, який ви пишете в JavaScript, є парадигмою наслідування прототипів. У JavaScript просто немає класичного успадкування. Це повинно трохи прояснити:
Inheritance
|
+-----------------------------+
| |
v v
Prototypal Classical
|
+------------------------------+
| |
v v
Prototypal Pattern Constructor Pattern
Як ви бачите, прототипне успадкування і класичне успадкування - це дві різні парадигми успадкування. Деякі мови, такі як Self, Lua та JavaScript, підтримують прототипне успадкування. Однак більшість мов, таких як C ++, Java та C # підтримують класичне успадкування.
Короткий огляд об'єктно-орієнтованого програмування
Як наслідування за прототипом, так і класичне наслідування є об'єктно-орієнтованими парадигмами програмування (тобто вони мають справу з об'єктами). Об'єкти - це просто абстракції, які інкапсулюють властивості реальної сутності (тобто вони представляють реальні слова в програмі). Це відомо як абстракція.
Абстракція: представлення реальних речей у комп'ютерних програмах.
Теоретично абстракцію визначають як "загальну концепцію, сформовану шляхом вилучення загальних рис із конкретних прикладів". Однак задля цього пояснення ми замість цього використаємо вищезгадане визначення.
Зараз деякі об’єкти мають багато спільного. Наприклад, грязьовий велосипед та Harley Davidson мають багато спільного.
Грязьовий велосипед:
А Харлі Девідсон:
Грязьовий велосипед і 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));
Однак зворотне не відповідає дійсності (тобто ви не можете використовувати класи для моделювання прототипів). Це тому, що прототипи - це об'єкти, але класи - не об'єкти. Вони є абсолютно різним типом абстракції.
Висновок
Підсумовуючи, ми дізналися, що абстракція - це "загальне поняття, що формується шляхом вилучення загальних рис із конкретних прикладів" і що узагальнення є "абстракцією більш конкретної абстракції" . Ми також дізналися про відмінності між прототиповим та класичним успадкуванням та про те, як вони обидва - дві грані однієї монети.
Зауважуючи, що хотілося б зауважити, що існує два зразки успадкування прототипів: прототипний візерунок та модель конструктора. Прототипний візерунок є канонічним шаблоном успадкування прототипів, тоді як конструкторський зразок використовується для того, щоб наслідування прототипів було схоже на класичне успадкування. Особисто я віддаю перевагу прототипному малюнку.
PS Я хлопець, який написав допис у блозі " Чому прототипічне спадкування має значення " і відповів на питання " Переваги спадкового прототипу над класичним? ". Моя відповідь - це прийнята відповідь.