класичне успадкування проти прототипічного успадкування у javascript


118

У мене є дуже багато посилань у Google, і я не можу скласти гарне уявлення про різницю між класичним успадкуванням та прототиповим успадкуванням?

Я навчився деяких речей з них, але все ще плутаю поняття.

Класичне успадкування

// Shape - superclass
function Shape() {
  this.x = 0;
  this.y = 0;
}

//superclass method
Shape.prototype.move = function(x, y) {
    this.x += x;
    this.y += y;
    console.info("Shape moved.");
};

// Rectangle - subclass
function Rectangle() {
  Shape.call(this); //call super constructor.
}

//subclass extends superclass
Rectangle.prototype = Object.create(Shape.prototype);

Чи класичне успадкування використовує прототипове успадкування всередині?

http://aaditmshah.github.io/why-prototypal-inheritance-matters/

З вищенаведеного посилання я дізнався, що ми не можемо додавати нові методи під час виконання класичної спадщини . Це правильно? Але ви можете перевірити вищевказаний код Я можу додати метод "переміщення" та будь-які методи під час виконання прототипу . Так це класичне успадкування, засноване на прототипі? Якщо так, що таке фактичне класичне успадкування та успадкування по прототипу? Мене з цим бентежить.

Прототипне успадкування.

function Circle(radius) {
    this.radius = radius;
}
Circle.prototype.area = function () {
    var radius = this.radius;
    return Math.PI * radius * radius;
};
Circle.prototype.circumference: function () {
    return 2 * Math.PI * this.radius;
};
var circle = new Circle(5);
var circle2 = new Circle(10);

Чи схоже це на класичне успадкування? Я повністю розгублений щодо того, що таке спадкове прототипи? Що таке класичне успадкування? Чому класична спадщина погана?

Чи можете ви надати мені простий приклад для кращого розуміння їх простим способом.

Дякую,

Сива


Дублікат, перевірити це: stackoverflow.com/questions/1595611/…
Silviu Burcea

5
не впевнений, про що тут йдеться - перший блок коду є прототиповим успадкуванням, а не класичним. Ваш другий блок коду взагалі не має спадщини!
Альнітак

Про це можна сказати
HasanAboShally

@alnitak developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… це посилання говорить про те, що одна була класичною спадщиною. ось чому я плутаюсь.
SivaRajini

Більш детально про те, чому ви хочете уникнути класичного успадкування, дивіться у моїй розмові "Класичне спадщину застаріло: як думати в прототипній ОО" vimeo.com/69255635
Ерік Елліотт

Відповіді:


248

Обидва зразки коду, які ви продемонстрували у своєму запитанні, використовують успадкування прототипів. Насправді будь-який об'єктно-орієнтований код, який ви пишете в 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);

На мою скромну думку, прототипові об'єктно-орієнтовані мови програмування є більш потужними, ніж класичні об'єктно-орієнтовані мови програмування, оскільки:

  1. Існує лише один тип абстракції.
  2. Узагальнення - це просто об'єкти.

Напевно ви вже зрозуміли різницю між класичним успадкуванням та прототиповим успадкуванням. Класичне успадкування обмежується класами, що успадковують від інших класів. Однак прототипічне успадкування включає не тільки прототипи, що успадковують від інших прототипів, але й об'єкти, що успадковують прототипи.


Прототип-клас Ізоморфізм

Напевно, ви помітили, що прототипи та класи дуже схожі. Це правда. Вони є. Насправді вони настільки схожі, що фактично можна використовувати прототипи для моделювання класів:

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 Я хлопець, який написав допис у блозі " Чому прототипічне спадкування має значення " і відповів на питання " Переваги спадкового прототипу над класичним? ". Моя відповідь - це прийнята відповідь.


2
дякую за чудову відповідь. мені потрібно зрозуміти, як прототипний шаблон краще порівняти з конструктором pattern.any приклад?
SivaRajini

1
Я написав порівняльну критику щодо конструкторів та прототипів у своєму блозі: aaditmshah.github.io/why-prototypal-inheritance-matters/…
Aadit M Shah

Отже, чи було б правильно сказати, що коли ми використовуємо функції в javascript для досягнення спадкування, ми дещо використовуємо класичну модель успадкування, а коли ми використовуємо звичайні об'єкти - це прототипне успадкування (обидва внутрішні після прототипічного успадкування)?
Swanidhi

1
@Swanidhi Ні. Якщо ви використовуєте JavaScript, ви використовуєте прототипову модель успадкування. Однак JavaScript має два аромати успадкування прототипів: використання функцій (тобто шаблон конструктора) та використання об'єктів (тобто прототипний шаблон).
Аадіт М Шах

5
@Swanidhi Ні. Це все ще прототип. У JavaScript немає "класів", а отже, абсолютно нічого в JavaScript не є класичним, включаючи конструктори. Це все ще прототипне успадкування. Просто дивна форма успадкування прототипів, яку люди плутають із класичною спадщиною. Коротше кажучи, programming with classes = classical inheritance, programming with prototypes = prototypal inheritance, programming with constructors = weird form of prototypal inheritance that looks a lot like classical inheritance. Надія, яка прояснює речі.
Аадіт М Шах

8

Перш ніж перейти до спадщини, ми розглянемо дві основні моделі для створення екземплярів (об'єктів) у javascript:

Класична модель: Об'єкт створений за планом (клас)

class Person {
  fn() {...}
} // or constructor function say, function Person() {}

// create instance
let person = new Person();

Прототипна модель: Об'єкт створюється безпосередньо з іншого об'єкта.

// base object
let Person = { fn(){...} }

// instance
let person = Object.create(Person);

У будь-якому випадку спадкування * досягається за допомогою зв’язування об'єктів за допомогою об'єкта прототипу.

(* Методи базового класу доступні через. похідний клас через об'єкт-прототип і не потрібно явно присутній у похідному класі.)

Ось гарне пояснення, щоб краще зрозуміти ( http://www.objectplayground.com/ )


0

Собака - тварина. Сюзанна - собака. У класичному успадкуванні Animal- це клас, Dogє підкласом Animalі suzannaє екземпляром а Dog.

У прототипічному успадкуванні класу немає. У вас є об'єкт animal, який є об'єктом. А dog- ще один об'єкт, який клонує і розширюється animal(об’єкт-прототип). suzannaє третім об'єктом, який копіює і розширює dog.

let animal = {hasChlorophyl: false};

let dog = Object.create(animal);
Object.assign(dog, {
  speak() {
    console.log("Woof!");
  }
});

let suzanna = Object.create(dog);
Object.assign(suzanna, {
  name: "Suzanna"
});

suzanna.speak();

Якщо ви пишете Dogзамість dog, особливо якщо ви робите Dogякусь функцію "конструктор", то ви не займаєтеся прототиповим успадкуванням; ви робите (псевдо-) класичну спадщину . Те, що ви використовуєте Object.create()для досягнення цього, не означає, що ви робите прототипне успадкування.

Насправді, JavaScript підтримує лише прототипне успадкування. Заплутаний newоператор і .prototypeатрибут є для того, щоб зробити прототипне успадкування схожим на (псевдо-) класичне успадкування.

Дуглас Крокфорд досліджує це в своїй книзі "JavaScript: хороші частини".

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.