很长时间以来,我认为“多态(Polymorphing)”是指将某种东西转变为🐑(因为魔兽)。羊的形象一直困扰我,使我难以确切地理解多态性。 (译者注:魔兽世界中,Polymorphing 是指将敌方单位变成一只羊的变形术。参考百度百科。中文读者应该不会有这个困扰)
今天,我想探究多态性实际上是什么。(滑稽的是:大多数有关 JavaScript 中的多态性的文章所涵盖的内容不到多态性实际内容的1/3)
什么是多态性?
Polymorphism 来自单词 Polymorph。
- Poly:很多
- Morph:从一种形式变成另一种
因此,多态性是采取多种形式的能力。
编程中有三种多态性:
- 特设多态性
- 参数多态性
- 子类型多态性
大多数有关面向对象编程和多态的文章仅解释了第三种类型。他们没有解释前两种。
特设(Adhoc)多态性
Adhoc 用于描述没有事先计划的事物的创建。换句话说,Adhoc 多态意味着在现场将某种东西从一种形式更改为另一种形式。
Adhoc 多态性有多种形式:
- 运算符重载
- 函数重载
- 强制转换多态性
运算符重载
重载意味着能够做多件事。
例:
JavaScript 中的+运算符可做很多事情。你可以使用它来对数字进行相加,也可以使用它来拼接字符串。
// Adding numbers
1 + 1 // Results in 2
// Adding Strings
'Hello' + ' ' + 'World' // Results in 'Hello World'
// Adding Numbers to Strings
1 + 'up' // Results in '1up'
结果的 type 会根据相加的内容而变化。
Number+Number=>NumberNumber+String=>String
这个例子中的 + 操作符可以将值从一种类型(如 Number)转变成另一种类型(如 String)。
函数重载
在某些编程语言中,函数重载意味着创建两个(或多个)同名函数。每个函数根据给定的参数执行不同的操作。
Wikipedia 中有关在 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)
}
// Overloading happens here
function calculateVolume (...args) {
if (args.length === 3) return volumeCuboid(...args)
return volumeCube(args[0])
}
我们不需要依赖参数的数量。我们还可以根据每个参数的值改变结果。
例子:
我们可以有一个 createShape 函数,这个函数根据 shape 的值返回不同的对象。(工厂模式使用这种类型的多态性)。
function createShape (size, shape) {
if (shape === 'triangle') return new Triangle(/* ... */)
if (shape === 'rectangle') return new Rectangle(/* ... */)
if (shape === 'square') return new Square(/* ... */)
}
(有趣的是:我从 Martin Fowler 的《重构:改进现有代码的设计》中了解了此版本的多态性。这使我对多态性更加好奇,最终使您阅读了这篇文章!)
如果我们进一步简化该理论,则所有 if 和 switch 语句都会导致函数重载。
function createEmoji (emotion) {
if (emotion === 'happy') return '😃'
if (emotion === 'sad') return '😞'
return 😑
}
强制转换多态性
JavaScript 有类型强制转换。它会在衡量时把值从一个类型转换为另一个类型。
例如,你可以在 if 语句中使用任何表达式。JavaScript 将表达式转换为 true 或 false。如果表达式转换为 true,则表示该表达式为真。如果表达式转换为 false,则表示该表达式为假。
const string = 'hello'
if (string) {
console.log(string)
}
另一个示例:你可以使用 == 对字符串和数字进行比较(尽管通常不建议这样做)。
22 == '22' // true
由于类型强制发生在现场临时发生的,因此它是 adhoc 多态性的一种形式。
变量重载?
我对这一点不太确定。
维基百科将多态定义为:
多态是为不同类型的实体提供单个接口,或者使用单个符号来表示不同类型
“用单个符号代表不同的类型”对我来说就像变量重载。(变量重载不是一个实际术语。这是我想出的东西)。
我们已经在 JavaScript 中重载了变量,因为每个变量都可以表示任何值。
// Variables in JavaScript can represent any value
const str = 'string'
const num = 123
const bool = true
const array = []
const obj = {}
const nah = null
参数多态
参数多态性是与参数相关的多态性……但这不是很有用,所以让我们描述一下它的含义。
参数多态性包括两个部分:
- 可以包含多种数据类型的数据
- 可以处理多种类型数据的函数
可以包含多种数据类型的数据
JavaScript 中的所有内容都是一个对象。因此对象是参数化的。可以将其转换为其他类型的数据。
对象还可以存储多种类型。不管存储什么值。
const object = {
str: 'hello',
num: 123,
bool: true
}
数组也是参数化的。它使您可以存储多种类型的数据,而不必关心它们是什么。
const array = ['hello', 123, true]
可以处理多种类型数据的函数
可以处理多种类型数据的函数称为多态函数。它们不在乎会发生什么,它们会应用被告知要做的转换,然后吐出结果。
map 就是一个很好的例子。它接受一个数组,然后吐出另一个数组。中间发生了什么无关紧要。
const doubled = [1, 2, 3].map(num => num * 2)
你可以使用 map 将数字转换为字符串。
const toString = [1, 2, 3].map(num => `${num}`)
Object.assign 是另一个例子。它接收一个对象并吐出另一个对象,但是它并不关心每个对象内部的内容。
Object.assign({}, { property: 'value'})
子类型多态
子类型多态涉及 从父对象创建派生对象。可以称为包含多态(Inclusion Polymorphism),子类化(Subclassing)或继承(Inheritance)。(继承是一个很容易理解的词,我将在之后解释)。
然后,派生对象可以覆盖父对象的方法,并且该方法仍然有效。
示例:
假设您有一个带有 sayHi 方法的 Human 类:
class Human {
constructor(name) {
this.name = name
}
sayHi() {
console.log(`Hi! My name is ${name}`)
}
}
然后,从 Human 创建 Developer 和 Designer 子类。
class Developer extends Human {/* ... */}
class Designer extends Human {/* ... */}
我们的 Designer 和 Developer 会更多地谈论他们自己,因此我们可以覆盖 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.`)
}
}
现在您有三个不同的类。他们每个人都可以 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.
这就是了!
小结
有三种多态性:
- 特设多态性
- 参数多态性
- 子类型多态性
很有可能,您已经在不了解多态的情况下使用它😉。我希望这可以为你描述清楚多态!