105

我已经阅读了一些我可以在互联网上找到的关于多态性的文章。但我想我不能完全理解它的含义和它的重要性。大多数文章都没有说明它为什么重要以及我如何在 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);
4

7 回答 7

109

多态性是面向对象编程(OOP)的原则之一。它是设计对象以共享行为并能够用特定行为覆盖共享行为的实践。多态性利用继承来实现这一点。

在 OOP 中,一切都被视为对象。这种抽象可以一直深入到汽车的螺母和螺栓,也可以像简单的带有年份、品牌和型号的汽车类型一样广泛。

为了有一个多态的汽车场景,需要有基本的汽车类型,然后会有继承自汽车的子类,并在汽车的基本行为之上提供自己的行为。例如,一个子类可能是 TowTruck,它仍然有一年的品牌和型号,但也可能有一些额外的行为和属性,这些行为和属性可能像 IsTowing 的标志一样基本,也可能像电梯的细节一样复杂。

回到人和员工的例子,所有的员工都是人,但所有的人都不是员工。也就是说,人是超类,员工是子类。人们可能有年龄和体重,但他们没有薪水。员工是人,所以他们天生就有年龄和体重,但也因为他们是员工,所以他们会有薪水。

所以为了方便,我们先写出超类(Person)

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

我们将赋予 Person 分享其信息的能力

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

我们将通过定义更适合 Employee 的行为来覆盖 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 的构造函数与 person 的构造函数非常相似,并且原型中唯一的函数被覆盖了。多态设计的力量在于共享行为。

于 2014-12-24T21:48:06.397 回答
29

正如另一个答案中所解释的,多态性有不同的解释。

我读过的关于这个主题的最佳解释是著名类型理论家Luca Cardelli的一篇文章。这篇文章名为On Understanding Types, Data Abstraction, and Polymorphism

它是什么?

Cardelli 在本文中定义了几种类型的多态性:

  • 普遍的
    • 参数
    • 包容
  • 特别指定
    • 超载
    • 强迫

也许在 JavaScript 中,要看到多态的影响有点困难,因为更经典的多态类型在静态类型系统中更为明显,而 JavaScript 具有动态类型系统。

因此,例如,JavaScript 在编译时没有方法或函数重载或自动类型强制。在动态语言中,我们认为其中大部分是理所当然的。由于语言的动态特性,我们在 JavaScript 中也不需要像参数多态这样的东西。

尽管如此,JavaScript 还是有一种类型继承的形式,它以类似于我们通常在其他面向对象编程语言(如 Java 或 C#)中所做的类似方式模拟子类型多态性(上面的 Cardelli 归类为包含多态性)的相同思想(如在我在上面分享的另一个答案)。

动态语言中非常典型的另一种多态形式称为鸭子类型

认为多态性只与面向对象编程有关是错误的。其他编程模型(函数式、过程式、逻辑等)在其类型系统中提供了不同形式的多态性,对于那些只用于 OOP 的人来说可能有点陌生。

为什么我们需要它?

多态性在软件中培养了许多良好的属性,除其他外,它培养了模块化和可重用性,并使类型系统更加灵活和可延展。没有它,就很难对类型进行推理。多态性确保一种类型可以被其他兼容的类型替换,前提是它们满足公共接口,因此这也促进了信息隐藏和模块化。

它是如何工作的?

这并不容易回答,不同的语言有不同的实现方式。在 JavaScript 的情况下,如上所述,您将看到它使用原型继承以类型层次结构的形式实现,您也可以使用鸭子类型来利用它。

这个主题有点宽泛,你在一篇文章中打开了两个很多问题。也许最好从阅读 Cardelli 的论文开始,然后尝试理解多态性,而不考虑任何语言或编程范式,然后您将开始在理论概念与任何特定语言(如 JavaScript)为实现这些想法所必须提供的东西之间建立关联。

于 2014-12-24T22:44:31.473 回答
16

多态性的目的是什么?

多态通过放宽类型等价的条件,使静态类型系统更加灵活,而不会失去(重要的)静态类型安全性。证据仍然是程序只有在不包含任何类型错误的情况下才会运行。

多态函数或数据类型比单态函数或数据类型更通用,因为它可以用于更广泛的场景。从这个意义上说,多态性代表了严格类型语言中的泛化思想。

这如何适用于 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"]

临时多态性(又名重载)

Ad-hoc 多态性表示不同类型仅对特定目的是等价的。为了在这个意义上是等价的,一个类型必须实现一组特定于该目的的函数。定义一个或多个特殊多态类型参数的函数需要知道哪些函数集与其每个参数相关联。

Ad-hoc 多态性使函数与更大的类型域兼容。以下示例说明了“映射”目的以及类型如何实现此约束。而不是一组函数,“可映射”约束只包含一个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属性,我们实际上是在比较苹果和橙子。

于 2016-11-26T09:43:00.940 回答
8

它是什么?

Poly=许多,态射=形式或行为转变。

为什么我们需要它?

在编程中,当我们希望函数(比如说函数 X)的接口足够灵活以接受不同类型或数量的参数时使用它。此外,基于改变参数类型或数量,我们可能希望函数 X 表现不同(态射)。

这个怎么运作?

我们编写了 X 函数的多个实现,其中每个实现接受不同的参数类型或参数数量。根据参数的类型或数量,编译器(在运行时)决定当从某些代码调用 X 时应该执行 X 的哪个实现。

如何在 javascript 中实现这种多态行为?

JS 不是一种类型化的语言,因此它实际上并不意味着使用像多态这样的 OOP 概念。然而,较新版本的 JS 现在包含类,并且多态性也有可能在 JS 中开始变得有意义。其他答案提供了一些有趣的解决方法。

于 2016-12-16T08:12:37.123 回答
6

多态性意味着能够在不同的对象上调用相同的方法并且每个对象以不同的方式响应称为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);

于 2018-08-28T11:19:39.210 回答
3

JavaScript 是一种解释型语言,而不是一种编译型语言。

编译时多态(或静态多态) 编译时多态不过是java、c++中的方法重载

所以在javascript中方法重载是不可能的。

但是动态(运行时)多态性是运行时存在的多态性,因此在javascript中方法覆盖是可能的

另一个例子是 PHP。

于 2017-05-04T07:44:31.990 回答
1

多态性是定义通用类型的行为的能力,当应用于不同的类型时会表现出不同的行为。

假设我们有一个实现 talk 方法的 Animal 类。如果类 Dog 和 Cat 继承自类 Animal 的 talk(),则对象 dog 和对象 cat 都会说话,但形式不同。

当我们迭代一个集合时,该集合必须支持可迭代协议。集合对象的形式是数组还是字典都没有关系。

+,-,*,/ 运算符是多态的。因为它们使用不同的类型:复数、小数、整数。

多态的主要好处是恢复代码。如果您有重复的代码,则需要额外的内存空间,这会降低性能,并且您还需要有人或某事来维护该重复的代码。

由于您重用单个代码,它有助于轻松调试您的代码

于 2021-07-17T03:03:32.270 回答