Что такое полиморфизм в Javascript?

92

Я прочитал возможную статью о полиморфизме, которую смог найти в Интернете . Но я думаю, что не мог полностью понять значение этого и его важность. В большинстве статей не говорится, почему это важно и как добиться полиморфного поведения в ООП (конечно, в 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.";
};

Далее мы хотим иметь подкласс Person, Employee.

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которое будет для Employee, так как оно выше в цепочке, чем Person. Это то, что я имел в виду, когда сказал «переопределить».
Travis J
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 точку полиморфизма можно лучше увидеть в примере с OP. функция 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функцию:

// 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 {}
);

Полиморфизм подтипа

Поскольку другие ответы уже охватывают полиморфизм подтипа, я пропускаю его.

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

Структурный полиморфизм говорит, что разные типы эквивалентны, если они содержат одну и ту же структуру таким образом, что один тип имеет все свойства другого, но может включать дополнительные свойства. При этом структурный полиморфизм - это утиная типизация во время компиляции и, безусловно, обеспечивает некоторую дополнительную безопасность типов. Но, утверждая, что два значения имеют один и тот же тип только потому, что они разделяют некоторые свойства, он полностью игнорирует семантический уровень значений:

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.

Апурв
источник