9

ES6之前,javascript本质上不能算是一门面向对象的编程语言,因为它对于封装、继承、多态这些面向对象语言的特点并没有在语言层面上提供原生的支持。
但是,它引入了原型(prototype)的概念,可以让我们以另一种方式模仿类,并通过原型链的方式实现了父类子类之间共享属性的继承以及身份确认机制。
其实,面向对象的概念本质上来讲不是指某种语言特性,而是一种设计思想。
正是由于javascript本身对面向对象编程没有一个语言上的支持标准,所以才有了五花八门、令人眼花缭乱的“类继承”的代码。
所以在ES6中出现了class extends等关键字,解决了javascript面向对象中出现了问题。
之前花了大量篇幅来讲述面向对象中的封装和继承
谈一谈javascript面向对象
javascript面向对象与原型
javascript 面向对象之一篇文章搞定call()方法")
javascript面向对象之继承(上)")
javascript面向对象之继承(下)")
javascript面向对象之ES6中的类和继承
今天我们研究一下javascript面向对象中的多态

javascript 多态

如果你在搜索引擎中搜索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() ); // 咯咯咯

多态的代码示例

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 Chicken());
makeSound(new Duck());

这个栗子出自《JavaScript设计模式与开发实践》,我手里没有这本书,但是不耽误我们去研究,这个?不太好扩展,我们继续看郭靖和黄蓉的栗子

    function doSth(hero) {
      hero.doSth();
    };

    function Gj() {
      this.name = "郭靖"
      this.favourite = "吃饭",
      this.skill = "降龙十八掌",
      this.sex="男"
    }
    Gj.prototype.doSth = function () {
      console.log(`${this.name}练习${this.skill}`);
    };


    function Hr() {
      this.name = "黄蓉"
      this.like = "做饭"
      this.skill = "打狗棒"
    }
    Hr.prototype.doSth = function () {
      console.log(`${this.name}练习${this.skill}`);
    };

    const gj = new Gj()
    const hr = new Hr()
    doSth(gj);//=>郭靖练习降龙十八掌
    doSth(hr);//=>黄蓉练习打狗棒

我们分析一下代码
郭靖(Gj)和黄蓉(Hr)都有自己的属性和方法,而且不一定是一一对应关系,(郭靖有的属性和方法,黄蓉不一定有,反之亦然,具体参见代码)
郭靖有name,favourite,skill,sex属性和在原型链上添加了doSth方法
黄蓉有name,like,skill属性和在原型链是上添加了doSth方法

脑洞一下这个场景
洪七公站在郭靖和黄蓉的面前,发号施令:“练功”,然后郭靖和黄蓉分别执行自己的doSth方法,进行练功,而且每个人修炼的武功不一样
因为郭靖和黄蓉都可以练功都有doSth方法,所以我们将这个方法抽离出来进行封装,这么做有什么好处呢?
如果我们不这么做,就得进行条件判断,类似于if else,如果是黄蓉就执行黄蓉的doSth,如果是郭靖就执行郭靖的doSth,如果再来一个人,还要继续判断,
当人数越来越多的时候,判断就会越来越多,如果把丐帮弟子都拉过来练功,想想会怎么样呢?

多态背后的思想是将“做什么”和“谁去做以及怎样去做”分离开来,也就是将“不变的事物”与“可能改变的事物”分离开来。
在这个栗子中,郭靖和黄蓉都会练功(都存在doSth),这是不变的,但是他们所修炼的武功是不同的,所具备的属性也不是完全相同的。把不变的部分隔离出来,把可变的部分封装起来,这给予了我们扩展程序的能力,程序看起来是可生长的,也是符合开放---封闭原则的,相对于修改代码来说,仅仅增加代码就能完成同样的功能,这显然优雅和安全得多。

这里有一个问题,如果新来的人没有doSth方法怎么办呢?
所以,我们需要添加一个判断

    function doSth(hero) {
      if (hero.doSth instanceof Function) {
        hero.doSth();
      }
    };

instanceof ???
instanceof用于判断一个变量是否是某个对象的实例
可以这么理解,上面的例子中hero.doSth是不是一个函数方法,如果是就执行
Martin Fowler 在《重构:改善既有代码的设计》里写到:

在电影的拍摄现场,当导演喊出“action”时,主角开始背台词,照明师负责打灯光,后面的群众演员假装中枪倒地,道具师往镜头里撒上雪花。在得到同一个消息时,每个对象都知道自己应该做什么。如果不利用对象的多态性,而是用面向过程的方式来编写这一段代码,那么相当于在电影开始拍摄之后,导演每次都要走到每个人的面前,确认它们的职业分工(类型),然后告诉他们要做什么。如果映射到程序中,那么程序中将充斥着条件分支语句。

利用对象的多态性,导演在发布消息时,就不必考虑各个对象接到消息后应该做什么。对象应该做什么并不是临时决定的,而是已经事先约定和排练完毕的。每个对象应该做什么,已经成为了该对象的一个方法,被安装在对象的内部,每个对象负责它们自己的行为。所以这些对象可以根据同一个消息,有条不紊地分别进行各自的工作。

将行为分布在各个对象中,并让这些对象各自负责自己的行为,这正是面向对象设计的优点。

多态增加了代码的可扩展性,降低了代码维护成本

图片描述

原文链接

参考链接
Javascript的继承与多态


陌上寒
1.9k 声望525 粉丝