前记

在维基百科中继承的含义是使子类别具有父类别的属性和方法,或者说子类构造的对象直接拥有父类对象的属性。

但是在JavaScript中并没有"子类"和"父类"的概念,也没有"类"(class)和"实例"(instance)的区分,JavaScript的继承是靠prototype(原型链)来实现的。

prototype继承实现

我曾写过一篇名为原型和原型链的文章,那时候说明了原型和原型链是什么,那继承只要在它的原型链中添加公有属性就可以了

假如我们写一个构造函数Human

function Human(name){
  this.name = name//构造函数可以生成新对象,this就是这个新对象
}

然后在Human的原型链上添加函数run

Human.prototype.run = function (){
  console.log(this.name + '在跑')
  return undefined
}

这样当我们用Human构造一个对象时候

let a = new Human('lgm')
//Human {name: "lgm"}
//  name: "lgm"/
//  __proto__:
//    run: ƒ ()
//    constructor: ƒ Human(name)
//     __proto__: Object

现在我们再写一个构造函数Man

function Man(name){
  this.name = name//this新生成的对象
  this.gender: '男'//this新生成的对象
}

然后在Man的原型链上添加函数fight

Man.prototype.fight = function (){
  console.log(this.name + '会打架')
}

这样用Man构造一个对象就是

let b = new Man('lgm')
//Man {name: "lgm"}
//  name: "lgm"/
//  gender: '男'
//  __proto__:
//    fight: ƒ ()
//    constructor: ƒ Man(name)
//      __proto__: Object

当我们想要Man继承Human的属性的时候,就可以知道,只要

Man.prototype.__proto__ = Human.prototype

修改Man

function Man(name){
  Human.call(this, name)
  this.gender: '男'//this新生成的对象
}

就可以完成继承了,让我们来试一下
再次构造一个Man对象

let lgm = new Man('lgm')
//Man {name: "lgm", gender: "男"}
//  gender: "男"
//  name: "lgm"/
//  __proto__: Human
//    fight: ƒ ()
//    constructor: ƒ Man(name)
//    __proto__:
//      run: ƒ ()
//      constructor: ƒ Human(name)
//      __proto__: Object

由此我们可以看到,Man继承了Human中的run()函数,但是有一个问题
直接操作__proto__是不属于ECMA规范的,在IE中直接操作并不可行,我们需要换一种写法

var f = function(){}
f.prototype = Human.prototype
Man.prototype = new f()

这样是使用了new的特性,让我们来试一下,然后再次构造一个Man对象

let lgm = new Man('lgm')
//Man {name: "lgm", gender: "男"}
//  gender: "男"
//  name: "lgm"/
//  __proto__: Human
//    fight: ƒ ()
//    __proto__:
//      run: ƒ ()
//      constructor: ƒ Human(name)
//      __proto__: Object

可以看到,效果是一样的

class语法糖

在ES6语法中,新增了一个叫做class的语法,专门用来实现继承,上面代码通过class改写如下

class Human {
  constructor(name) {
    this.name = name
  }
  run() {
    console.log("我叫" + this.name + ",我在跑")
    return undefined
  }
}
class Man extends Human {//Man.prototype.__proto__ = Human.prototype
  constructor(name) {
    super(name)//Human.call(this, name)
    this.gender = '男'
  }
  fight() {
    console.log(this.name + '会打架')
  }
}

两种方法的优劣

prototype继承写法麻烦,但是简单易懂,而class继承的写法虽然简单,但难以理解

并且,原型链继承添加继承属性的话会会非常简单,只需

Human.prototype.headNumber = '1'

而class继承如果需要添加继承属性会非常麻烦


mrlgm
54 声望6 粉丝

好好学习,天天向上