什么是 Javascript 中的多态性?

我读了一些可能的文章,我可以在互联网上找到的 多态性。但是我想我不能完全理解它的意义和它的重要性。大多数文章都没有说明它为什么重要,以及我如何在 OOP 中实现多态行为(当然是在 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);
113412 次浏览

Polymorphism is one of the tenets of Object Oriented Programming (OOP). It is the practice of designing objects to share behaviors and to be able to override shared behaviors with specific ones. Polymorphism takes advantage of inheritance in order to make this happen.

In OOP everything is considered to be modeled as an object. This abstraction can be taken all the way down to nuts and bolts for a car, or as broad as simply a car type with a year, make, and model.

To have a polymorphic car scenario there would be the base car type, and then there would subclasses which would inherit from car and provide their own behaviors on top of the basic behaviors a car would have. For example, a subclass could be TowTruck which would still have a year make and model, but might also have some extra behaviors and properties which could be as basic as a flag for IsTowing to as complicated as the specifics of the lift.

Getting back to the example of people and employees, all employees are people, but all people are not employees. Which is to say that people will be the super class, and employee the sub class. People may have ages and weights, but they do not have salaries. Employees are people so they will inherently have an age and weight, but also because they are employees they will have a salary.

So in order to facilitate this, we will first write out the super class (Person)

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

And we will give Person the ability to share their information

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

Next we wish to have a subclass of Person, Employee

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

And we will override the behavior of getInfo by defining one which is more fitting to an Employee

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

These can be used similar to your original code use

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


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

However, there isn't much gained using inheritance here as Employee's constructor is so similar to person's, and the only function in the prototype is being overridden. The power in polymorphic design is to share behaviors.

As explained in this other answer, polymorphism has different interpretations.

The best explanation on the subject that I've ever read is an article by Luca Cardelli, a renowned type theorist. The article is named On Understanding Types, Data Abstraction, and Polymorphism.

What Is it?

Cardelli defines several types of polymorphism in this article:

  • Universal
  • parametric
  • inclusion
  • Ad-hoc
  • oveloading
  • coercion

Perhaps in JavaScript, it is a bit more difficult to see the effects of polymorphism because the more classical types of polymorphism are more evident in static type systems, whereas JavaScript has a dynamic type system.

So, for instance, there is no method or function overloading or automatic type coercions at compile time in JavaScript. In a dynamic language, we take most of these things for granted. Neither we need something like parametric polymorphism in JavaScript due to the dynamic nature of the language.

Still, JavaScript has a form of type inheritance that emulates the same ideas of subtype polymorphism (classified as inclusion polymorphism by Cardelli above) in a similar way to what we typically do in other object-oriented programing languages like Java or C# (as explained in another answer I shared above).

Another form of polymorphism very typical in dynamic languages is called duck typing.

It is a mistake to believe that polymorphism is only related to object-oriented programming. Other programming models (functional, procedural, logic, etc.) offer different forms of polymorphism in their type systems, probably in a way a bit unfamiliar to those only used to OOP.

Why We Need It?

Polymorphism foster many good attributes in software, among other things it fosters modularity and reusability and makes the type system more flexible and malleable. Without it, it would be really difficult to reason about types. Polymorphism makes sure that one type can be substituted by other compatible ones provided that they satisfy a public interface, so this also fosters information hiding and modularity.

How Does it Work?

This is not simple to answer, different languages have different ways to implement it. In the case of JavaScript, as mentioned above, you will see it materialize in the form of type hierarchies using prototypal inheritance and you can also exploit it using duck typing.

The subject is a bit broad and you opened too many questions in a single post. Perhaps it is best that you start by reading Cardelli's paper and then try to understand polymorphism irrespective of any language or programming paradigm, then you will start making associations between the theoretical concepts and what any particular language like JavaScript has to offer to implement those ideas.

What is the purpose of polymorphism?

Polymorphism makes a static type system more flexible without losing (significant) static type safety by loosening the conditions for type equivalence. The proof remains that a program will only run if it doesn't contain any type errors.

A polymorphic function or data type is more general than a monomorphic one, because it can be used in a wider range of scenarios. In this sense polymorphism represents the idea of generalization in strictly typed languages.

How does this apply to Javascript?

Javascript has a weak, dynamic type system. Such a type system is equivalent with a strict type system containing only one type. We can think of such a type as a huge union type (pseudo syntax):

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

Every value will be associated to one of these type alternatives at run-time. And since Javascript is weakly typed, every value can change its type any number of times.

If we take a type theoretical perspective and consider that there is only one type, we can say with certainty that Javascript's type system doesn't have a notion of polymorphism. Instead we have duck typing and implicit type coercion.

But this shouldn't keep us from thinking about types in our programs. Due to the lack of types in Javascript we need to infer them during the coding process. Our mind have to stand in for the missing compiler, i.e. as soon as we look at a program we must recognize not only the algorithms, but also the underlying (maybe polymorphic) types. These types will help us to build more reliable and more robust programs.

In order to do this properly I am going to give you an overview of the most common manifestations of polymorphism.

Parametric polymorphism (aka generics)

Parametric polymorphism says that different types are interchangeable because types doesn't matter at all. A function that defines one or more parameters of parametric polymorphic type must not know anything about the corresponding arguments but treat them all the same, because they can adopt to any type. This is quite restricting, because such a function can only work with those properties of its arguments that are not part of their data:

// 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"]

Ad-hoc polymorphism (aka overloading)

Ad-hoc polymorphism says that different types are equivalent for a specific purpose only. To be equivalent in this sense a type must implement a set of functions specific to that purpose. A function that defines one or more parameters of ad-hoc polymorphic type then needs to know which sets of functions are associated to each of its arguments.

Ad-hoc polymorphism makes a function compatible to a larger domain of types. The following example illustrates the "map-over" purpose and how types can implement this constraint. Instead of a set of function the "mappable" constraint only includes a single map function:

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

Subtype polymorphism

Since other answers already cover subtype polymorphism I skip it.

Structural polymorphism (aka strutrual subtyping)

Structural polymorphism says that different types are equivalent, if they contain the same structure in such a way, that one type has all the properties of the other one but may include additional properties. That being said, structural polymorphism is duck typing at compile time and certainly offers some additional type safety. But by claiming that two values are of the same type just because they share some properties, it completely ignores the semantic level of values:

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

Unfortunately, speed is considered a subtype of weight and as soon as we compare the value properties we are virtually comparing apples with oranges.

what is it?

Poly= many, morphism=form or behavior shifting.

why we need it ?

In programming, It is used when we want a function's (let say function X's) interface to be flexible enough to accept different types or number of parameters. Also, based on changing parameters types or numbers, we might want the function X to behave differently (morphism).

How it works?

We write multiple implementations of X function where each implementation accepts different parameters types or number of parameters. Based on the type or number of parameter, the compiler (at runtime) decides which implementation of X should be executed when X is called from some code.

how can I achieve this polymorphic behavior in javascript?

JS is not a typed language so it really not meant to use OOP concepts like polymorphism. However, the newer version of JS now include classes and there is possibility that polymosphism could start making sense in JS, too. Other answers provide some interesting workarounds.

JavaScript is an interpreted language, not a compiled language.

Compile time Polymorhism( or Static polymorphism) Compile time polymorphism is nothing but the method overloading in java,c++

So method overloading is not possible in javascript.

But Dynamic (run time) polymorphism is the polymorphism existed at run-time so method overriding is possible in javascript

another example is PHP.

Polymorphism means Ability to call the same method on different objects and each object responds in different way is called POLYMORPHISM.

    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);

Polymorphism is the ability to define a generic type of behaviour that will behave differently when applied to different types.

Let's say we have an Animal class that implements the talk method. If class Dog and Cat inherit talk() from class Animal, object dog and object cat both will talk but in a different form.

When we iterate over a collection, that collection has to support the iterable protocol. It does not matter the form of the collection object whether it is an array or dictionary.

+,-,*,/ operators are polymorphic. Because they work with different types: complex numbers, decimal, integer.

The main benefit of polymorphism is resuing the code. If you have duplicate code, you need extra space in memory which decreases the performance and also you need someone or something to maintain that duplicate code.

Since you reusing the single code, it helps debug your code easily