JS如何实现继承

92 阅读2分钟

使用原型链

有以下代码:

function Animal(legsNumber) {
    this.legsNumber = legsNumber
}
Animal.prototype.kind = '动物'

function Dog(name, age) {
    this.name = name
    this.age = age
}
Dog.prototype.kind = '狗'
Dog.prototype.eat = function () {
    console.log(`我是${this.name},我在狂吃狂吃狂吃狂吃狂吃狂吃`);
}

var 大毛 = new Dog('大毛', 4)
大毛.eat()

上面代码包含Animal()和Dog()函数,如何让Dog()继承Animal()呢?

function Animal(legsNumber) {
    this.legsNumber = legsNumber
}
Animal.prototype.kind = '动物'

function Dog(name, age) {
    Animal.call(this, 4) // 关键代码1,相当于给Dog增加legsNumber属性,相当于执行了:this.legsNumber = 4
    this.name = name
    this.age = age
}

// 关键代码2,给Dog绑定原型,但是这句代码并不规范,不同浏览器指向原型的属性名不一样(有些浏览器用'__proto__',有些浏览器用'[[prototype]],总之并不统一'),怎么解决?
Dog.prototype.__proto__ = Animal.prototype

Dog.prototype.kind = '狗'
Dog.prototype.eat = function () {
    console.log(`我是${this.name},我在狂吃狂吃狂吃狂吃狂吃狂吃`);
}
var 大毛 = new Dog('大毛', 4)
大毛.eat()

上面的代码做了以下两件事:

关键代码1: 执行Animal.call(this, 4)来给Dog绑定相关属性,增加了legsNumber属性,相当于执行了:this.legsNumber = 4;

关键代码2: 给Dog绑定原型。但是这句代码并不规范,不同浏览器指向原型的属性名不一样(有些浏览器用'proto',有些浏览器用'[[prototype]],总之并不统一'),怎么解决?

想一想解决方案:关键代码2其实是改变原型,那么如何能改变原型?有两种方法,一个是Object.create(),还有一个是new关键字,前者只能在ES6中使用,所以考虑用new关键字,可以改成:Dog.prototype = new Animal()。

但是这样子改又存在一个问题,new会返回一个带有legsNumber属性的对象(不懂原因的可以参考一下我的这篇文章:JS的new做了什么?),这样子相当于在Dog.prototype上面多了一个legsNumber属性,那么如何解决这个问题呢:

var f = function () {} // 
f.prototype = Animal.prototype
Dog.prototype = new f()

上面代码相当于把Animal()的函数体改为空,这样子就避免new()的时候去绑定了不必要的参数,也就是去掉了Dog.prototype上的legsNumber。所以完整的代码:

function Animal(legsNumber) {
    this.legsNumber = legsNumber
}
Animal.prototype.kind = '动物'

function Dog(name, age) {
    Animal.call(this, 4) // 关键代码1,相当于给Dog增加legsNumber属性,相当于执行了:this.legsNumber = 4
    this.name = name
    this.age = age
}

// 关键代码2,给Dog绑定原型,但是这句代码并不规范,不同浏览器指向原型的属性名不一样(有些浏览器用'__proto__',有些浏览器用'[[prototype]],总之并不统一'),怎么解决?
// Dog.prototype.__proto__ = Animal.prototype
// 想一想解决方案:上面代码其实是改变原型,那么如何能改变原型?有两种方法,一个是Object.create(),还有一个是new关键字,前者只能在ES6中使用,所以考虑用new关键字
// Dog.prototype = new Animal()
// 上面代码又存在一个问题,new会返回一个带有legsNumber属性的对象(不懂原因的可以参考一下我的这篇文章:JS的new做了什么?),这样子相当于在Dog.prototype上面多了一个legsNumber属性,那么如何解决这个问题呢?
var f = function () {}
f.prototype = Animal.prototype
Dog.prototype = new f()

Dog.prototype.kind = '狗'
Dog.prototype.eat = function () {
    console.log(`我是${this.name},我在狂吃狂吃狂吃狂吃狂吃狂吃`);
}
var 大毛 = new Dog('大毛', 4)
大毛.eat()

使用class

使用class继承可以直接使用extends关键字:

class Animal {
    constructor(legsNumber) {
        this.legsNumber = legsNumber
    }
}
class Dog extends Animal {
    constructor(name, age) {
        super(4)
        this.name = name
        this.age = age
    }
    eat() {
        console.log(`我是${this.name},我在狂吃狂吃狂吃狂吃狂吃狂吃`);
    }
}
var 大毛 = new Dog('哮天', 5)
大毛.eat()

完。