[翻译]javascript的多态性问题

原文:https://zellwk.com/blog/polymorphism-javascript/

一、什么是多态性

多态性来自单词Polymorph。

  • Poly: 许多。
  • Morph: 从一种形态变为另一种形态

所以多态是指具有多种形式的能力;

编程中有三种多态性
1、特定多态性(仅支持有限数量的不同类型)
2、参数多态性
3、子类型多态性
大部分面向对象编程的文章只解释第三种多态类型,很少提到另外两种。

二、特定多态(Adhoc polymorphism)

特定Adhoc用来描述在没有事先计划的情况下做某事。换句话说,Adhoc polymorphism意味着当场将某物从一种形式改变为另一种形式;有很多Adhoc polymorphism的类型:
1、Operator Overloading 操作符重载
2、Function Overloading 函数重载
3、Coercion Polymorphism 强制多态

(一)、Operator Overloading
重载意味着能够做不止一件事。
比如:
‘+’运算符在javascript中可以做很多事情。你可以用它来加数字,也可以用它来连接字符串。

1 + 1 // 2
'hello' + ' ' + 'world'  // hello world

结果的类型取决于加运算的类型

  • number + number = number
  • number + string = string
    所以+运算操作允许将值从一种类型 (如number)转换成另一种类型(如string)

(二)、Function Overloading
在某些编程语言中,函数重载意味着创建两个(或多个)具有相同名称的函数。每个函数根据给定的参数执行不同的操作。
比如c++中的例子:

// Volume of a Cube.
int Volume(int s) {
  return s * s * s;
}

// Volume of a Cuboid.
long Volume(long l, int b, int h) {
  return l * b * h;
}

但是在javascript中的函数重载稍微不同因为我们不可以声明两个名字相同的不同函数;我们使用一个函数,但是我们可以根据接收到的参数不同改变返回的结果。上面的例子可以用javascript写成如下:

function volumeCuboid (length, breadth, height) {
  return length * breadth * height
}

function volumeCube (length) {
  return volumeCuboid(length, length, length)
}


// 这里实现函数重载
function calculateVolume (...args) {
  if (args.length === 3) return volumeCuboid(...args)
  return volumeCube(args[0])
}

我们不需要依赖于参数的数量。我们还可以根据每个参数的值改变结果;如下面的函数例子:

function createShape (size, shape) {
  if (shape === 'triangle') return new Triangle(/* ... */)
  if (shape === 'rectangle') return new Rectangle(/* ... */)
  if (shape === 'square') return new Square(/* ... */)
}

作者的想法来自:Martin Fowler 的书Improving the Design of Existing Code

如果我们进一步提取这个理论,所有的If和switch语句都会导致函数重载。

(三)、Coercion Polymorphism
JavaScript具有类型强制。在求值时将值从一种类型转换为另一种类型。
例如在if语句中的表达式,js可以将表达式转换成true 或者 false。

const string = 'hello'
if (string) {
  console.log(string)
}

另一个例子:您可以用==比较字符串和数字(尽管通常不推荐这样做)

22 == '22' // true

因为类型强制是当场发生的,所以它是特定多态性的一种形式

(四)、Variable overloading? 变量重载?

“Use a single symbol to represent different types” feels like Variable overloading to me. (Variable Overloading is not an actual term. It’s something I came up with).
We already overload variables in JavaScript since each variable can represent any value.

三、参数多态性

参数多态性是与参数相关的多态性……但这不是很有用,所以让我们来描述一下它是关于什么的。
1.可以包含多种数据类型的数据
2.可以处理多种类型数据的函数(Functions that can work with many types of data)

(一)可以包含多种数据类型的数据

JavaScript中的一切都是对象。所以Objects是参数化的。它可以被转换成其他类型的数据。
对象还可以存储多种类型。它不关心存储的值是什么。数组也是同样的。

const object = {
  str: 'hello',
  num: 123,
  bool: true
}

(二)可以处理多种类型数据的函数

可以处理多种类型数据的函数称为多态函数。他们不关心传进来的是什么。它们会应用已知的变换,然后得到一个结果。
map就是一个很好的例子。它接收一个数组并输出另一个数组。它不关心中间是什么。Object.assign 也是同样的,接收一个对象并输出一个对象。

const doubled = [1, 2, 3].map(num => num * 2)

四、子类型多态性

子类型多态性涉及从父对象创建派生对象。它可以被称为包含多态、子类化或继承。
派生对象可以覆盖父对象的方法,它仍然可以工作。
比如:

1、有一个sayhi方法的human类
class Human {
  constructor(name) {
    this.name = name
  }

  sayHi() {
    console.log(`Hi! My name is ${name}`)
  }
}
2、你可以创建子类Developer 、Designer
class Developer extends Human {/* ... */}
class Designer extends Human {/* ... */}
3、这两个子类说自己比较多,所以我们重写sayhi方法
class Developer extends Human () {
  sayHi() {
    console.log(`Hi! My name is ${name}. I am a developer.`)
  }
}

class Designer extends Human () {
  sayHi() {
    console.log(`Hi! My name is ${name}. I am a designer.`)
  }
}
4、现在你有三个不同的类,每一个类都有一个sayhi方法,你可以正常使用sayhi而且都是正常工作,但是他们却可以输出不同的结果
const zell = new Human('Zell')
const vincy = new Developer('Vincy')
const tim = new Designer('Tim')

zell.sayHi() // Hi! My name is Zell.
vincy.sayHi() // Hi! My name is Vincy. I am a developer.
tim.sayHi() // Hi! My name is Tim. I am a designer.

四、总结(Wrapping up)

有三种多态性。

  • 特别的多态性
  • 参数多态性
  • 子类型多态性
    实际上是你已经在不知情的情况下使用多态~希望你更能理解多态~