Наскільки прототипне успадкування практично відрізняється від класичного успадкування?


27

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

Однак я не можу зрозуміти, в чому їх відмінність з точки зору практичного використання? Іншими словами, коли ви визначаєте базовий клас (прототип), а потім отримуєте з нього деякі підкласи, ви обидва маєте доступ до функціоналітів базового класу, і ви можете розширювати функції на похідних класах. Якщо ми розглядаємо те, що я сказав, як намічений результат успадкування, то чому ми повинні турбуватися, якщо ми використовуємо прототипний чи класичний варіант?

Щоб зрозуміти себе більше, я не бачу різниці у корисності та моделях використання прототипічного та класичного успадкування. Це призводить до того, що я не маю інтересу дізнатися, чому вони різні, оскільки вони обидва призводять до одного і того ж, OOAD. Чим практично (не теоретично) прототипне успадкування відрізняється від класичного успадкування?

Відповіді:


6

Останні повідомлення в блозі про JS OO

Я вважаю, що ваше порівняння - це класична емуляція OO в JavaScript та класична OO, і, звичайно, ви не бачите різниці.

Відмова від відповідальності: замініть всі посилання на "прототипічний OO" на "прототиповий OO в JavaScript". Я не знаю специфіки Self або будь-якої іншої реалізації.

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

Для прототипічного ОО немає поняття інкапсуляції. Інкапсуляція - це особливість сфери застосування, закриття та функцій першого класу, але не має нічого спільного з прототиповим OO. Також немає поняття успадкування, те, що люди називають "спадщиною", насправді є просто поліморфізмом.

Однак він має поліморфізм.

Наприклад

var Dog = {
  walk: function() { console.log("walks"); }
}

var d = Object.create(Dog);
d.walk();

Очевидно, dмає доступ до методу, Dog.walkі це виявляє поліморфізм.

Тож насправді велика різниця . У вас є лише поліморфізм.

Однак, як було сказано, якщо ви хочете зробити це (я не маю уявлення, чому ви це зробите), ви можете імітувати класичний OO в JavaScript і мати доступ до (обмеженого) інкапсуляції та успадкування.


1
@Rayons, Google для прототипічного успадкування, і ви побачите, що з'являється прототипне успадкування . Навіть від Yahoo!
Саїд Неаматі

@Saeed успадкування - це невиразний термін, який часто використовується. Що вони означають під «спадщиною», я позначаю «поліморфізм». Визначення занадто розпливчасті.
Райнос

Ні @Rayons, я мав на увазі, що слово prototypal є правильним терміном, а не прототипним . Я не говорив про спадщину :)
Saeed Neamati

1
@Saeed, що це одна з таких помилок, що мій погляд просто проганяє. Я не
звертаю

1
Хм .. термінологія. ритм. Я б назвав те, як об’єкти javascript пов'язані з їх прототипами, "делегуванням". Поліморфізм, як мені здається, викликає занепокоєння нижчого рівня, виходячи з того, що дане ім’я може вказувати на різний код для різних об'єктів.
Шон Макміллан

15

Класичне успадкування успадковує поведінку, без будь-якого стану, від батьківського класу. Він успадковує поведінку в той момент, коли об'єкт інстанціюється.

Прототипне успадкування успадковує поведінку і стан від материнського об'єкта. Він успадковує поведінку та стан у той момент, коли об'єкт викликається. Коли батьківський об’єкт змінюється під час виконання, це впливає на стан та поведінку дочірніх об'єктів.

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


4
На практиці, якщо ви успадковуєте державу, ви налаштовуєте себе на світ поранення. Спадковий стан стане загальним, якщо він живе на іншому об'єкті.
Шон Макміллан

1
Мені здається, що в javascript насправді немає поняття поведінки, окремо від стану. Крім функції конструктора, вся поведінка може бути призначена / модифікована створенням об'єктів, як і будь-який інший стан.
Joeri Sebrechts

Тож у Python немає класичної спадщини? Якщо я маю class C(object): def m(self, x): return x*2і instance = C()тоді, коли бігаю, instance.m(3)отримую 6. Але якщо я потім Cтак змінюсь C.m = lambda s, x: x*xі забіжу, instance.m(3)тепер отримую 9. Те саме стосується, якщо я роблю class D(C)і змінюю метод, Cто будь-які екземпляри Dотримують також змінений метод. Я нерозумію чи це означає, що Python не має класичної спадщини за вашим визначенням?
mVChr

@mVChr: ​​Ви все ще успадковуєте поведінку, а не заявляєте в цьому випадку, що вказує на класичне успадкування, але це вказує на проблему з моїм визначенням "успадковує поведінку в момент, коли об'єкт інстанціюється". Я не зовсім впевнений, як виправити визначення.
Joeri Sebrechts

9

По-перше: більшу частину часу ви будете використовувати об’єкти, не визначаючи їх, а використання об'єктів однаково в обох парадигмах.

По-друге: Більшість прототипових середовищ використовують той же тип поділу, що і середовища на основі класу - змінні дані про екземпляр, з методами, що передаються у спадок. Тож знову дуже мало різниці. (Дивіться мою відповідь на це запитання щодо переповнення стека та " Самостійні документи з організації книг без класів" . Дивіться у Citeseer для версії PDF .)

По-третє: Динамічна природа Javascript надає набагато більший вплив на тип успадкування. Те, що я можу додати новий метод до всіх екземплярів типу, призначивши його базовому об'єкту, є акуратним, але я можу зробити те ж саме в Ruby, повторно відкривши клас.

По-четверте: практичні відмінності невеликі, в той час як практичне питання забути використовувати newнабагато більше - тобто на вас набагато більше шансів на те, що пропустіть, newніж на вас вплине різниця між прототиповим і класичним кодом .

Все, що сказано, практична відмінність прототипічного від класичного успадкування полягає в тому, що ваші способи (класифіковані) речі (класи) такі ж, як і ваші речі-дані-утримуйте (екземпляри.) Це означає, що ви можете збирати свої класи поступово, використовуючи всі ті ж інструменти для маніпулювання об'єктом, які ви використовували б у будь-якому випадку. (Це насправді, як це роблять усі бібліотеки емуляції класів. Для підходу не зовсім схожого на всі інші дивіться на Traits.js ). Це в першу чергу цікаво, якщо ви програмуєте метапрограмування.


так, найкраще відповідь ІМО!
Айвар

0

Прототипне успадкування в JavaScript відрізняється від класів цими важливими способами:

Конструктори - це просто функції, за якими можна телефонувати без new:

function Circle (r, x, y) { 
  //stuff here
}
Var c = new Circle();
Circle.call(c, x, y, z); //This works and you can do it over and over again.

Приватних змінних чи методів немає, в кращому випадку ви можете це зробити:

function Circle (r, x, y) {
  var color = 'red';
  function drawCircle () {
    //some code here with x, y and r
  }
  drawCircle();
  this.setX = function (x_) {
    x = x_;
    drawCircle();
  }

}
Circle.prototype.getX = function () {
   //Can't access x!
}

У попередньому прикладі ви не можете значно розширити клас, якщо ви вдаєтеся до підроблених приватних змінних та методів, а крім того, будь-які відкриті вами відкриті методи будуть відтворюватися щоразу, коли створюється новий екземпляр.


Ви можете продовжити "клас" змістовно. Ви просто не можете отримати доступ до локальних змінних color, r, x, yі drawCircle, прив'язаних до лексичної областіCircle
Raynos

Це правда, ви можете, але ви не можете цього зробити, не створивши купу "публічних" методів, доданих до контексту, що створюються щоразу, коли ви створюєте екземпляр.
Бьорн

Це дуже дивна модель, яку ви використовуєте там. Circle.call()як конструктор? Схоже, ви намагаєтесь описати "функціональні об'єкти" (неправильне слово ...)
Шон Макміллан

Я ніколи не робив це, я просто кажу, що можна викликати конструктор знову і знову за допомогою JavaScript, але не мовами з традиційними класами.
Bjorn

1
Але як ці відмінності "важливі"? Я не бачу побудови об'єктів за допомогою виклику функції, не використовуючи "new" як "важливий", лише невелику різницю синтаксису. Крім того, відсутність приватних змінних принципово не відрізняється, лише невелика різниця. Крім того, ви можете мати (щось подібне до) приватні змінні у своєму описі.
Roel
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.