Що таке поліморфізм у Javascript?


91

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

Я не можу навести жодного прикладу коду, оскільки я не маю ідеї, як його реалізувати, тому мої запитання наведені нижче:

  1. Що це?
  2. Навіщо нам це потрібно?
  3. Як це працює?
  4. Як я можу досягти цієї поліморфної поведінки в javascript?

Я отримав цей приклад. Але легко зрозуміло, яким буде результат цього кодексу. Це не дає чіткого уявлення про сам поліморфізм.

function Person(age, weight) {
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo.";
    }
}
function Employee(age, weight, salary) {
    this.salary = salary;
    this.age = age;
    this.weight = weight;
    this.getInfo = function() {
        return "I am " + this.age + " years old " +
        "and weighs " + this.weight +" kilo " +
        "and earns " + this.salary + " dollar.";
    }
}

Employee.prototype = new Person();
Employee.prototype.constructor = Employee;
  // The argument, 'obj', can be of any kind
  // which method, getInfo(), to be executed depend on the object
  // that 'obj' refer to.

function showInfo(obj) {
    document.write(obj.getInfo() + "<br>");
}

var person = new Person(50,90);
var employee = new Employee(43,80,50000);
showInfo(person);
showInfo(employee);

Це питання, мабуть, занадто широке, щоб добре працювати з StackOverflow. Найкраще, що ми могли б зробити, це пов’язати вас з якимсь іншим поясненням поліморфізму. StackOverflow найкраще відповідає на конкретні запитання або роз'яснення щодо конкретної проблеми, наприклад "Джерело сказав, що поліморфізм був XYZ, але що означає Y?"
Вітрувій

2
вам це не потрібно. зовсім. вам навіть не потрібні класи в JS, і насправді існує багато інших, можливо, кращих, парадигм для побудови додатків. apply / call / bind усуває необхідність однорідності, а за допомогою soft-object ви можете змінити будь-що відповідно до своїх потреб, не попередньо декоруючи це або успадковуючи особливі випадки.
dandavis

1
Поліморфізм пов'язаний не просто з ОО, і він має багато значень. Можливо, ви захочете прочитати цю іншу відповідь під питаннями Чи можливий поліморфізм без успадкування .
Едвін Далорцо,

Для успадкування зазвичай виконується неправильно в JavaScript. Створення екземпляра Parent, який буде використовуватися як прототип Child, демонструє недостатнє розуміння ролі функції конструктора та прототипу у визначенні та створенні об'єкта. Більше інформації доступно у цій відповіді: stackoverflow.com/a/16063711/1641941
HMR

Відповіді:


99

Поліморфізм є одним із принципів об’єктно-орієнтованого програмування (ООП). Це практика проектування об’єктів для спільної поведінки та можливості заміщення спільної поведінки з конкретними. Поліморфізм використовує спадщину, щоб це здійснити.

В ООП все вважається змодельованим як об’єкт. Цю абстракцію можна взяти аж до гайок і болтів для автомобіля, або настільки широкий, як просто тип автомобіля з роком, маркою та моделлю.

Щоб мати поліморфний сценарій автомобіля, існував би базовий тип автомобіля, а потім були б підкласи, які успадковуватимуть від автомобіля та забезпечуватимуть свою власну поведінку поверх базової поведінки автомобіля. Наприклад, підкласом може бути TowTruck, який все одно матиме рік виготовлення та модель, але також може мати деякі додаткові особливості поведінки та властивості, які можуть бути настільки ж базовими, як прапор для IsTowing, настільки складними, як особливості підйомника.

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

Отже, щоб полегшити це, ми спочатку випишемо супер клас (Person)

function Person(age,weight){
 this.age = age;
 this.weight = weight;
}

І ми надамо Персону можливість ділитися своєю інформацією

Person.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo.";
};

Далі ми хочемо мати підклас Особа, Працівник

function Employee(age,weight,salary){
 this.age = age;
 this.weight = weight;
 this.salary = salary;
}
Employee.prototype = new Person();

І ми замінимо поведінку getInfo, визначивши такий, який більше підходить співробітнику

Employee.prototype.getInfo = function(){
 return "I am " + this.age + " years old " +
    "and weighs " + this.weight +" kilo " +
    "and earns " + this.salary + " dollar.";  
};

Їх можна використовувати подібно до використання оригінального коду

var person = new Person(50,90);
var employee = new Employee(43,80,50000);

console.log(person.getInfo());
console.log(employee.getInfo());

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


2
@ user3138436 - Це правильно. Прототипний ланцюжок буде перевірений, на перший випадок getInfoякого буде співробітник, оскільки він вище в ланцюжку, ніж персональний. Це було те, що я мав на увазі, коли сказав "перевизначений".
Тревіс Дж.

17
Ви не використовуєте конструктор (Person.call (this, arg)) і не встановлюєте прототип Employee на екземпляр Person. Прототип - це місце, де переходять спільні члени, а функція конструктора - це місце, де створюються конкретні члени екземпляра. Ваші зразки використовують код копіювання та повторне використання функції конструктора і успадковують частину прототипу неправильно (Person має конкретні члени екземпляра, які не мають бізнесу на службі Employee.prototype, особливо якщо у вас є змінні члени). Більше інформації про успадкування за допомогою функцій конструктора та прототипу тут: stackoverflow.com/a/16063711/1641941
HMR

3
Щоб співробітник міг повторно використовувати та розширювати getinfo людини, це міг просто зробити return Person.prototype.getInfo.call(this) + + "and earns " + this.salary + " dollar.";Замість копіювання коду повторного використання.
HMR

3
тут, де застосовується поліморфізм?
Альберт Джегані

2
@rpeg точку поліморфізму можна побачити краще на прикладі ОП. функція showInfo (); приймає загальний об'єкт. Поліморфізм зараз - це здатність по-різному реагувати залежно від типу об’єкта. Я думаю, що ця відповідь не робить цього досить зрозумілим, оскільки вона викликає getInfo () для кожного конкретного об'єкта.
Стефан

26

Як пояснюється в цій іншій відповіді , поліморфізм має різні тлумачення.

Найкраще пояснення з цього приводу, яке я коли-небудь читав, - стаття Луки Карделлі , відомого теоретика типу. Стаття має назву Про розуміння типів, абстрагування даних та поліморфізм .

Що це?

У цій статті Карделлі визначає кілька типів поліморфізму:

  • Універсальний
    • параметричний
    • включення
  • Спеціальна
    • затяжний
    • примус

Можливо, в JavaScript трохи складніше побачити наслідки поліморфізму, оскільки більш класичні типи поліморфізму більш очевидні в системах статичного типу, тоді як JavaScript має систему динамічного типу.

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

Тим не менше, JavaScript має форму успадкування типу, яка наслідує однакові ідеї поліморфізму підтипу (класифікований як поліморфізм включення Карделлі вище) подібним чином до того, що ми зазвичай робимо в інших об'єктно-орієнтованих мовах програмування, таких як Java або C # (як пояснено в ще одна відповідь, якою я поділився вище).

Інша форма поліморфізму, дуже типова для динамічних мов, називається качиним типом .

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

Навіщо нам це потрібно?

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

Як це працює?

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

Тема трохи широка, і ви відкрили два численні запитання в одному дописі. Можливо, найкраще спочатку прочитати статтю Карделлі, а потім спробувати зрозуміти поліморфізм незалежно від будь-якої мови чи парадигми програмування, тоді ви почнете встановлювати асоціації між теоретичними концепціями та тим, що може запропонувати будь-яка конкретна мова, наприклад JavaScript, для реалізації цих ідей.


14

Яка мета поліморфізму?

Поліморфізм робить систему статичного типу більш гнучкою, не втрачаючи (значну) безпеку статичного типу, послаблюючи умови для еквівалентності типу. Доказом залишається те, що програма працюватиме лише в тому випадку, якщо вона не містить помилок типу.

Поліморфна функція або тип даних є більш загальними, ніж мономорфні, оскільки вони можуть бути використані в більш широкому діапазоні сценаріїв. У цьому сенсі поліморфізм представляє ідею узагальнення в строго типових мовах.

Як це стосується Javascript?

Javascript має слабку, динамічну систему типу. Така система типів еквівалентна системі суворого типу, що містить лише один тип. Ми можемо уявити такий тип як величезний тип об’єднання (псевдосинтаксис):

type T =
 | Undefined
 | Null
 | Number
 | String
 | Boolean
 | Symbol
 | Object
 | Array
 | Map
 | ...

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

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

Але це не повинно заважати нам думати про типи в наших програмах. Через відсутність типів у Javascript нам потрібно зробити висновок про них під час процесу кодування. Наш розум повинен стати на захист відсутнього компілятора, тобто, як тільки ми подивимося на програму, ми повинні розпізнати не тільки алгоритми, але й основні (можливо, поліморфні) типи. Ці типи допоможуть нам створити більш надійні та надійні програми.

Щоб зробити це належним чином, я збираюся дати вам огляд найпоширеніших проявів поліморфізму.

Параметричний поліморфізм (він же дженерики)

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

// parametric polymorphic functions

const id = x => x;

id(1); // 1
id("foo"); // "foo"

const k = x => y => x;
const k_ = x => y => y;

k(1) ("foo"); // 1
k_(1) ("foo"); // "foo"

const append = x => xs => xs.concat([x]);

append(3) ([1, 2]); // [1, 2, 3]
append("c") (["a", "b"]); // ["a", "b", "c"]

Спеціальний поліморфізм (він же перевантаження)

Спеціальний поліморфізм говорить, що різні типи еквівалентні лише для певної мети. Щоб бути еквівалентним у цьому сенсі, тип повинен реалізувати набір функцій, специфічних для цієї мети. Тоді функція, яка визначає один або кілька параметрів спеціального поліморфного типу, повинна знати, які набори функцій асоційовані з кожним з її аргументів.

Спеціальний поліморфізм робить функцію сумісною з великим доменом типів. Наступний приклад ілюструє мету "map-over" і те, як типи можуть реалізувати це обмеження. Замість набору функцій обмеження "mappable" включає лише одну mapфункцію:

// Option type
class Option {
  cata(pattern, option) {
    return pattern[option.constructor.name](option.x);
  }
  
  map(f, opt) {
    return this.cata({Some: x => new Some(f(x)), None: () => this}, opt);
  }
};

class Some extends Option {
  constructor(x) {
    super(x);
    this.x = x;
  }
};

class None extends Option {
  constructor() {
    super();
  }
};


// ad-hoc polymorphic function
const map = f => t => t.map(f, t);

// helper/data

const sqr = x => x * x;

const xs = [1, 2, 3];
const x = new Some(5);
const y = new None();

// application

console.log(
  map(sqr) (xs) // [1, 4, 9]
);

console.log(
  map(sqr) (x) // Some {x: 25}
);

console.log(
  map(sqr) (y) // None {}
);

Підтип поліморфізму

Оскільки інші відповіді вже стосуються політипу підтипу, я пропускаю його.

Структурний поліморфізм (він же структурний підтип)

Структурний поліморфізм говорить, що різні типи еквівалентні, якщо вони містять однакову структуру таким чином, що один тип має всі властивості іншого, але може включати додаткові властивості. Тим не менш, структурний поліморфізм є типовим типом під час компіляції і, безсумнівно, пропонує деяку додаткову безпеку типу. Але, стверджуючи, що два значення одного типу лише тому, що вони мають деякі властивості, він повністю ігнорує семантичний рівень значень:

const weight = {value: 90, foo: true};
const speed =  {value: 90, foo: false, bar: [1, 2, 3]};

На жаль, speedце вважається підтипом, weightі як тільки ми порівнюємо valueвластивості, ми фактично порівнюємо яблука з апельсинами.


1
Хоча це багато в чому точніше (і, безумовно, більш ретельне), ніж прийнята відповідь, воно не має однакової доступності: ця відповідь передбачає, що той, хто задає запитання, вже надто розумний, щоб турбуватися питанням :)
Джаред Сміт

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

редагування значно покращує доступність. Що стосується корисності поліморфізму в динамічних мовах, є багато, але я намагаюся придумати хороший приклад в JS. Кращим прикладом можуть бути магічні методи Python, які дозволяють визначеним користувачем типам працювати з такими поліморфними функціями len. Або, можливо, conjвід кложуре.
Джаред Сміт

8

що це?

Полі = багато, морфізм = зміна форми або поведінки.

навіщо це нам потрібно?

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

Як це працює?

Ми пишемо кілька реалізацій функції X, де кожна реалізація приймає різні типи параметрів або кількість параметрів. На основі типу або номера параметра компілятор (під час виконання) вирішує, яку реалізацію X слід виконати, коли X викликається з якогось коду.

як я можу досягти цієї поліморфної поведінки в javascript?

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


4

Поліморфізм означає здатність викликати один і той же метод на різних об'єктах, і кожен об'єкт реагує по-різному, називається ПОЛІМОРФИЗМ .

    function Animal(sound){
    this.sound=sound;
    this.speak=function(){
    			return this.sound;
    	}
    }
//one method 
    function showInfo(obj){
    		console.log(obj.speak());
    }
//different objects
    var dog = new Animal("woof");
    var cat = new Animal("meow");
    var cow = new Animal("humbow");
//responds different ways
    showInfo(dog);
    showInfo(cat);
    showInfo(cow);


2

JavaScript - це інтерпретована мова, а не компільована мова.

Поліморфізм часу компіляції (або статичний поліморфізм) Поліморфізм часу компіляції - це не що інше, як перевантаження методу в Java, c ++

Отже, перевантаження методів неможливе у javascript.

Але динамічний (час виконання) поліморфізм - це той поліморфізм, який існував під час виконання, тому перевизначення методу можливо в javascript

інший приклад - PHP.

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