面向对象的三个基本特征是:封装、继承、多态。

多态

多态的实际含义是:同一操作作用于不同的对象上面,可以产生不同的解释和不同的执行结果。换句话说,给不同的对象发送同一个消息的时候,这些对象会根据这个消息分别给出不同的反馈。

一段“多态”的JavaScript代码

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

这段代码确实体现了“多态性”,当我们分别向鸭和鸡发出“叫唤”的消息时,它们根据此消息作出了各自不同的反应。但这样的“多态性”是无法令人满意的,如果后来又增加了一只动物,比如狗,显然狗的叫声是“汪汪汪”,此时我们必须得改动makeSound函数,才能让狗也发出叫声。修改代码总是危险的,修改的地方越多,程序出错的可能性就越大,而且当动物的种类越来越多时,makeSound有可能变成一个巨大的函数。

多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与 “可能改变的事物”分离开来。

在这个例子中,动物都会叫,这是不变的,但是不同类型的动物具体怎么叫是可变的。把不变的部分隔离出来,把可变的部分封装起来,这给予了我们扩展程序的能力,程序看起来是可生长的,也是符合开放-封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。

对象的多态性

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

var Duck = function(){};

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

var Chicken = function(){};

Chicken.prototype.sound = function() {
    console.log( '咯咯咯' );
};

makeSound(new Duck());        //嘎嘎嘎
makeSound(new Chicken());    //咯咯咯

现在我们向鸭和鸡都发出“叫唤”的消息,它们接到消息后分别作出了不同的反应。如果有一天动物世界里又增加了一只狗,这时候只要简单地追加一些代码就可以了,而不用改动以前的makeSound函数,如下所示:

var Dog = function(){};
 
Dog.prototype.sound = function() {
    console.log( '汪汪汪' );
};
 
makeSound(new Dog());     //汪汪汪

使用继承得到多态效果

    function Animal(sound) {
        this.class = animal;
        console.log(sound);
    }
    var duck = new Animal('嘎嘎嘎');
    var chicken = new Animal('咯咯咯');

    //它们都继承了class=animal; 通过参数输出了不同的声音;
    duck();
    chicken(); 

这里只列举了一个简单的使用继承得到多态效果,更多继承案例,移步上一篇面向对象的三个基本特征之继承

多态的最根本好处在于,你不必再向对象询问“你是什么类型”而后根据得到的答案调用对象的某个行为——你只管调用该行为就是了,其他的一切多态机制都会为你安排妥当。

换句话说,多态最根本的作用就是通过把过程化的条件分支语句转化为对象的多态性,从而消除这些条件分支语句。