什么是Javascript中的多态?

问题描述 投票:74回答:6

我已经阅读了一些我可以在互联网上找到的关于多态性的文章。但我认为我无法理解它的含义及其重要性。大多数文章没有说明为什么它很重要以及如何在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);
javascript oop functional-programming polymorphism parametric-polymorphism
6个回答
87
投票

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

在OOP中,一切都被认为是一个对象。这种抽象可以一直用于汽车的螺母和螺栓,或者仅仅是带有年份,品牌和型号的汽车类型。

为了拥有多态汽车场景,将会有基本汽车类型,然后会有从汽车继承的子类,并在汽车的基本行为之上提供自己的行为。例如,子类可能是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();

我们将通过定义一个更适合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的构造函数非常相似,因此使用继承获得的内容并不多,并且原型中唯一的函数被覆盖。多态设计的力量是分享行为。


23
投票

正如this other answer所解释的,多态性有不同的解释。

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

它是什么?

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

  • 普遍 参数 包容
  • 特设 oveloading 强迫

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

因此,例如,在JavaScript中编译时没有方法或函数重载或自动类型强制。在动态语言中,我们将大部分内容视为理所当然。由于语言的动态特性,我们都不需要JavaScript中的参数多态。

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

在动态语言中非常典型的另一种形式的多态性称为duck typing

认为多态性仅与面向对象编程相关是错误的。其他编程模型(功能,程序,逻辑等)在其类型系统中提供不同形式的多态性,可能对于仅用于OOP的人来说有点不熟悉。

为什么我们需要它?

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

它是如何工作的?

这不是一个简单的答案,不同的语言有不同的方式来实现它。对于JavaScript,如上所述,您将看到它使用prototypal inheritance以类型层次结构的形式实现,您也可以使用duck typing来利用它。

这个主题有点宽泛,你在一篇文章中打开了两个问题。也许最好先阅读Cardelli的论文,然后尝试理解多态性而不管任何语言或编程范式,然后你将开始在理论概念和JavaScript之类的任何特定语言之间建立关联来实现这些想法。


8
投票

多态的目的是什么?

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

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

这如何适用于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多态性表示不同类型仅对特定目的是等效的。在这种意义上,类型必须实现一组特定于该目的的函数。定义ad-hoc多态类型的一个或多个参数的函数然后需要知道哪些函数集与其每个参数相关联。

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


7
投票

它是什么?

Poly =很多,态射=形式或行为转移。

为什么我们需要它?

在编程中,当我们想要一个函数(比如函数X)接口足够灵活以接受不同类型或数量的参数时,使用它。此外,基于更改参数类型或数字,我们可能希望函数X的行为不同(态射)。

这个怎么运作?

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

我怎样才能在javascript中实现这种多态行为?

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


2
投票

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

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

因此,在javascript中无法进行方法重载。

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

另一个例子是PHP。


0
投票

多态性意味着能够在不同的对象上调用相同的方法,并且每个对象以不同的方式响应称为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);
© www.soinside.com 2019 - 2024. All rights reserved.