ES5的继承方式以及ES6的继承方式的区别

501 阅读3分钟

JS中没有类的概念,所有的类本质都是function。ES5和ES6的继承机制本质上都是依靠原型链,只是ES5要显式地声明,ES6使用语法糖class来表示。

ES5继承方式

要研究OOP中的继承,我们首先要从原型链这一概念讲起。通过原型链,我们可以是实现一定的继承,但是并不完美,因为原型链有两个问题。

  1. 当原型链中包含引用类型值的原型时,该引用类型的值会被所有实例共享。
  2. 当创建子类的实例时,我们无法向父类的构造函数中传递参数。

借用构造函数

为了解决这个问题,我们可以使用借助构造函数方法,它的实现原理是在子类的构造函数中调用父类的构造函数。

function Parent(){
    this.numbers = [1,2,3];
}
function Child(){
    Parent.call(this);
}

var child1 = new Child();
child1.numbers.push(4);
console.log(child1.numbers) // 1,2,3,4

var child2 = new Child();
console.log(child2.numbers) // 1,2,3

显然,借助父类的构造函数我们一举解决了上述两个问题:一是保证了原型链中引用类型的值的独立,二是子类在创建时也能给父类传递参数。但是这种方法也带来了问题,那就是方法是在构造函数中定义的,那么函数复用将变得不可能,父类定义的方法子类无法访问。为了解决这个问题,经典的ES5继承方式组合继承出现了。

组合继承

组合继承的基本思路: 使用原型链实现对原型属性和方法的继承,通过借用构造函数来实现对实例属性的继承.

// ES5继承 组合继承(应用最广)

function Parent(value){
    this.value = value;
}

Parent.prototype.getValue = function(){
    console.log(this.value);
}

function Child(value){
    Parent.call(this, value);  // 继承实例属性,调用父类构造函数
}

Child.prototype = new Parent(); // 继承父类方法,再次调用父类构造函数

这是经典的继承写法,融合了借助构造函数和原型链的优点,但是存在一个问题,那就是Parent类的构造函数被调用了两次。有没有什么办法避免这样呢?答案肯定是有的。

寄生组合式继承

寄生组合式继承就是为了减少调用父类构造函数的开销而出现的。其原理在于不必为了指定子类型的原型而调用超类型的构造函数。

function extend(Child,Parent){
	var prototype = object(Parent.prototype);//创建对象
	prototype.constructor = Child;//增强对象
	Child.prototype = prototype;//指定对象
}

ES6继承方式

ES6继承的实现方法只有一种标准模式,这比ES5更加清晰明了。首先,我们需要为父类和子类添加class关键字,虽然这只是一个语法糖。另外,extends关键字表明了父类和子类之间的继承关系。另一个特殊点就是constructor和super,子类必须在其构造函数中使用super方法。因为子类没有自己的this对象, 子类通过super方法获得父类的this对象。

class Animal{
    constructor(props){
        this.name = props.name || 'unknown';
    }
    
    eat(){
        console.log(this.name + " will eat pests");
    }
}

class Bird extends Animal{
    constructor(props, myProps){
        super(props);
        this.type = props.type || 'unknown';
        this.attr = myProps;
    }
    
    fly(){
        console.log(this.name + " can fly!");
    }
    
    show(){
        console.log(this.type + "..." + this.attr);
    }
}

var bird = new Bird({
    name: "Jack",
    type: "小麻雀"
}, "I am a bird.");
bird.eat();
bird.fly();
bird.show(); 
// Jack will eat pests
// Jack can fly!
// 小麻雀...I am a bird