ES5的继承
以下,我们假设父类为Animal,拥有name和age属性,以及run方法。同时设子类为Dog。Animal类的构造函数代码如下:
function Animal(name,age){
this.name=name;
this.age=age;
}
Animal.prototype.run=function(){
console.log(this.name+'is running')
}
法1:构造函数继承
子类中,利用函数的call,apply以及bind的方法,将父类构造函数中的this指向自身。以call为例,具体代码如下:
function Dog(name,age){
Animal.call(this,name,age);
}
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog1=new Dog('Jack',7);
console.log(dog1);
dog1.run();
此时会发现,该实例的属性名称得到继承,并根据其构造函数实参而设为了对应的值。但是发现子类实例化对象没有父类的run方法,这说明通过引用构造函数继承时,子类仅得到父类的属性,而并未继承得到父类的方法。(此处方法都写入构造函数的prototype中,如果方法作为构造函数的属性值,则能继承该方法)
法2:原型继承
将子类的prototype指向父类的实例化对象。具体代码如下:
function Dog(name, age) {
}
Dog.prototype = new Animal();
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码如下:
const dog1 = new Dog('Bob', 10);
console.log(dog1)
dog1.run();
此时可以发现,通过原型继承结果仅继承了父类的方法,而无法拿到父类的属性名。这与构造函数继承结果恰恰相反。实参只能写入父类实例化对象中才可获取到对应属性的值,但此时的属性与属性值也不是子类实例化对象自身的属性与属性值。
法3:组合继承
通过观察构
造函数继承与原型继承可以发现,二者继承内容互补,那么同时运用这两种继承方法,就可以同时拿到父类的属性与方法了。具体代码如下:
function Dog(name,age) {
Animal.call(this,name,age);
}
Dog.prototype=new Animal();
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog1 = new Dog('Amy', 9);
console.log(dog1)
dog1.run();
通过运行结果可以看到,虽然通过组合继承能拿到父类的属性与方法,但在继承父类方法的同时又一次继承了父类的属性,造成了不必要的赘余。
法4:寄生继承
通过调用Object的create方法,该方法实参为父类的prototype,并将这个新生成的对象赋给子类的prototype属性。(注:prototype本质上是一个对象)具体代码如下:
function Dog(name,age) {
Animal.call(this,name,age);
}
Dog.prototype=Object.create(Animal.prototype);
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog1 = new Dog('Tom', 5);
console.log(dog1)
dog1.run();
可以发现此时相较于组合继承,赘余的父类属性消失。
ES6的继承
ES6的继承使用extends关键字实现。此处仍以Animal为父类,Dog为子类。具体代码如下:
class Animal {
constructor(name, age) {
this.name = name;
this.age = age;
}
run = function () {
console.log(this.name + " is running")
}
}
class Dog extends Animal {
}
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog=new Dog('Ken',7);
console.log(dog)
dog.run()
super关键字与重写
1.如果子类用的是自己的构造函数,则需要使用super关键字调用父类的构造函数。
子类创建的代码如下:
class Dog extends Animal {
constructor(name,age,weight){
super(name,age);
this.weight=weight;
}
}
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog=new Dog('Jane',11,'5kg');
console.log(dog)
dog.run()
如果不使用super关键字,则会报错,提示必须使用super构造函数:
2.如果子类拥有自己的方法,且该方法与父类某个方法同名,则调用该方法时会调用子类的方法而非父类的方法,这就叫方法的重写。
子类创建的代码如下:
class Dog extends Animal {
constructor(name,age){
super(name,age)
}
run=function(){
console.log(this.name + " has run")
}
}
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog=new Dog('John',4);
console.log(dog)
dog.run()
3.如果子类想调用父类的方法,则需要使用super关键字。
子类创建的代码如下:
class Dog extends Animal {
constructor(name,age){
super(name,age)
}
run(){
super.run()
}
}
实例化Dog子类,并打印输出该实例,接着调用该实例的run方法,代码以及结果如下:
const dog=new Dog('Mike',5);
console.log(dog)
dog.run()