js 多态如何理解,最好能有个例子

最近读到一本书《JavaScript设计模式与开发实践》上,讲到js的多态,我在JavaScript高级程序编程里貌似都没有见过关于这个的详细讲解,所以想问问大家有没有什么推荐的文章或者博客,可以推荐给小弟的,让小弟可以深入了解一下。
先把那本上的例子拿出来跟大家分享:
书里面的故事:本人家里养了一只鸡,一只鸭。当主人向他们发出‘叫’的命令时。鸭子会嘎嘎的叫,而鸡会咯咯的叫。转化成代码形式如下

非多态代码示例

var makeSound = function(animal) {
    if(animal instanceof Duck) {
        console.log('嘎嘎嘎');
    } else if (animal instanceof Chicken) {
        console.log('咯咯咯');
    }
}
var Duck = function(){}
var Chiken = function() {};
makeSound(new Chicken());
makeSound(new Duck());

多态的代码示例

var makeSound = function(animal) {
    animal.sound();
}

var Duck = function(){}
Duck.prototype.sound = function() {
    console.log('嘎嘎嘎')
}
var Chiken = function() {};
Chiken.prototype.sound = function() {
    console.log('咯咯咯')
}

makeSound(new Chicken());
makeSound(new Duck());

多态背后的思想是将”做什么“和”谁去做以及怎样去做分开“。

阅读 18.7k
7 个回答
新手上路,请多包涵

到底什么是多态?这本身就是一个抽象的概念。宽泛的,你可以这么理解:

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。(某某百科)
本质上也就如题主说描述的“将‘做什么’和‘谁去做以及怎样去做’分开”,归根结底就是先消除不同对象的耦合关系。

举个让对象表示出多态性的最常用的例子说明一下:

//使用es6 class简化代码
class Parent {
    sayName() {
        console.log('Parent');
    }
}
class Child extends Parent{
    sayName() {
        console.log('Child');
    }
}
var child = new Child();
child.sayName(); // 'Child'

var parent = new Parent();
parent.sayName(); // 'Parent'

上面代码中,我想要child对象和parent对象分别调用告诉我他们的名字(name),他们分别调用告诉我名字的方法(sayName)却得到了不同的结果。尽管这实际上是在子类对象原型上创建了与父类对象原型同名的属性和方法。但这就是一个多态。

多态分为编译时多态和运行时多态。其中编辑时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编辑之后会变成两个不同的函数,在运行时谈不上多态。而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们所说的多态性。

由于JavaScript并不存在方法重载,并且在继承方面也与传统的接口/类不同,所以在JavaScript中大多是通过子类重写父类方法的方式实现多态,就像上面这段代码一样,但是也可以通过一些别的方式来实现多态,毕竟这只是一种思想或者说是模式,比如包括但不仅限于这种方法:

//以下代码基于上述代码
function sayAge(object) {
    if ( object instanceof Child ){
        console.log( '10' );
    }else if ( object instanceof Parent ){
        console.log( '30' );
    }
}

sayAge(child);   // '10'
sayAge(parent);  // '30'

不使用重写,通过if/else的判断,我们也得到了一个多态的实现:我想让他们告诉我他们的年龄(age),他们调用让我得到年龄的方法(sayAge)分别得到了不同的结果,这也是一个多态。只是这个多态具有较差的可扩展性而已,当然你可以改写sayAge,以获得更高的可扩展性:

function sayAge(object) {
    object.prototype.sayAge();
}

当改写成这样,你没发现这实际上就是在一个对象原型上创建了与另一个对象原型同名的属性和方法吗?

其实这种偏向设计模式的东西已经超出我这个渣渣的能力范围,这不是看一本书做一道题就能解决的,等有新的想法了我再来补充。
思考的方式可能存在问题,还请批评指正。

同一个实现接口,使用不同的实例而执行不同的操作。比如:打印机可以看作是父类,黑白打印机、彩色打印机是他的两个子类。父类打印机中的方法“打印”在每个子类中有各自不同的实现方式,比如对黑白打印机执行打印操作后,打印效果是黑白的,而对彩色打印机进行打印操作后,打印效果是彩色的。

鸭子类(Duck Type)是Mixin-Pattern的结果,就是将A的特性(方法+字段)赋予到B中,而A与B均对此无感知,唯一感知的就是调用者C而已,并且强调A与B并不能严格地抽象为同一个类别,仅形似而神不似。
由于JS是动态类型脚本语言,因此Duck Type在运用上符合多态的特性。
若要遵循多态定义中,子类重写父类方法,并且子类示可任意例替代父类实例的调用,则要采用JS中delegate-base inheritance来实现多态了。
具体可参考http://www.cnblogs.com/fsjohnhuang/p/4634039.html

我换个角度提下我个人的小建议。多态是程序设计模式中重中之重的问题,深入学习设计模式,也就明白什么是多态了。

这种是通过该构造函数判断的。要想通过参数类型 和 参数个数 可以用arguments,js不支持重载,没必要这样

Rabbit.prototype.toString = function() {
  return `a ${this.type} rabbit`;
};

console.log(String(blackRabbit));
// → a black rabbit

This is a simple instance of a powerful idea. When a piece of code is written to work with objects that have a certain interface—in this case, a toString method—any kind of object that happens to support this interface can be plugged into the code, and it will just work.

This technique is called polymorphism.

调用同一个的方法,却能得到不同的实现和结果,这种现象就是多态。原因就是这个方法的作用就是调用其它方法,或者说这个方法将具体逻辑的定义权交给外部,自己只负责调用,而不考虑实现。

举例:只要部署了 iterator 接口的数据结构,都可以使用 for of 循环。for of 的设计就运用了多态的思想。

参考:《Eloquent JavaScript》chapter 6

新手上路,请多包涵

其实就是个回调,源代码不动,在接口里处理逻辑,例子上的sound就是接口

1 篇内容引用
推荐问题
宣传栏