JS继承

111 阅读2分钟

1.基于原型的继承

在JS的中,各个对象都存在一条原型链,这些对象因为存在某些共有属性而划分为不同的类型,比如Array。将这些共有属性提取出来后,放入这些对象的__proto__中,便实现了属性的继承。
可以总结为:

对象.__proto__ === 其构造函数.prototype复制代码

现在我们有下面的代码

function Person(name, age){
    this.name = name
    this.age = age
}
Person.prototype.sayHi = function(){ //在Person的原型上定义属性
  console.log('你好,我是'+this.name)
} 

看了上面的例子应该很好理解,我们先定义了函数Person,再在Person的圆形上定义sayHi,那么sayHi这个函数不仅继承了Person的属性,还有自己本身的属性,那么我们后面定义的对象,再去调用sayHi时,就能够打印出同事拥有这两个函数属性的值,这就是基于原型的继承。

2.基于 class 的继承

在ES6中,我们的Class之间可以通过extends关键字实现继承(在 JS 中并不存在类,class 的本质就是函数)。
还是同样的代码,我们用class来实现一下:

class Person{
  constructor(name, age){
      this.name = name
      this.age = age
  }
  sayHi(){
      console.log('你好,我是'+this.name)
  }
}

两端代码比较一下就可以看见,class的定义包含了构造函数constructor和定义在原型对象上的函数sayHi()(注意没有function关键字),是不是避免了Person.prototype.sayHi = function() {...}这样分散的代码。
用class定义对象的另一个优点就是继承更方便了。现在我们从Person派生一个OnePerson。现在原型对象的构造函数等等都不需要考虑了,可以通过extends来实现:

class OnePerson extends Person {
    constructor(name, age) {
        super(name); // 记得用super调用父类的构造方法!
        this.age = age;
    }
 
    myAge() {
        alert('I am' + this.age);
    }
}

此时我们运行代码

let person2 = new OnePerson('jack', 19)
person2.name === 'jack' // true
person2.age === 19 // true
person2.myAge() // 打印出「I am 19」

OnePerson的定义也是class关键字实现的,而extends则表示原型链对象来自Person。子类的构造函数可能会与父类不太相同,例如,OnePerson需要name和age两个参数,并且需要通过super(name)来调用父类的构造函数,否则父类的name属性无法正常初始化。OnePerson已经自动获得了父类Person的sayHi方法,我们又在子类中定义了新的myAge方法。