js继承梳理

204 阅读2分钟

js继承应该也有好多种,最常用的是构造函数继承,原型继承,组合式继承,es6中的方法继承。

一、构造函数继承

function Parent(){
    this.name = "parent";
    this.colors = ["red","blue","yellow"];
}
function Child(){
    Parent.call( this ); // 或apply
    this.type = "child";
}

通过这种调用,把父类构造函数的this指向为子类实例化对象引用,从而导致父类执行的时候父类里面的属性都会被挂载到子类的实例上去。
Parent.prototype.sex = "男";
Parent.prototype.say = function() {
    console.log(" Oh,My God! ");
}
new Child().sex; // undefined
// Uncaught TypeError: (intermediate value).say is not a function
new Child().say();

缺点:Child无法继承Parent的原型对象,并没有真正的实现继承(部分继承)

二、原型链方法的继承

function Parent1(){
    this.name = "parent1";
    this.colors = ["red","blue","yellow"];
}
function Child1(){
    this.name = "child1";
}
Child1.prototype = new Parent1();
Parent1.prototype.sex = "男";
Parent1.prototype.say = function() {
    console.log(" Oh,My God! ");
}

new Child1().sex; //  男
new Child1().say(); // Oh,My God!
var s1 = new Child1();
s1.colors.push("black");
var s2 = new Child1();

s1.colors; // (4) ["red", "blue", "yellow", "balck"]
s2.colors; // (4) ["red", "blue", "yellow", "balck"]

我们实例化了两个Child1,在实例s1中为父类的colors属性push了一个颜色,但是s2也被跟着改变了。造成这种现象的原因就是原型链上中的原型对象它俩是共用的。

这不是我们想要的,s1s2这个两个对象应该是隔离的,这是这种继承方式的缺点。

三、组合式继承

这里所谓的组合是指组合借用构造函数和原型链继承两种方式。
function Parent2(){
    this.name = "parent2";
    this.colors = ["red","blue","yellow"];
}
function Child2(){
    Parent2.call(this);
    this.type = "child2";
}
Child2.prototype = new Parent2()
var s1 = new Child2();
s1.colors.push("black");
var s2 = new Child2();

s1.colors; // (4) ["red", "blue", "yellow", "balck"]
s2.colors; // (3) ["red", "blue", "yellow"]

四、es6继承

class Parent {
}
class Child1 extends Parent {
    constructor(x, y, colors) {
         super(x, y); // 调用父类的constructor(x, y)
         this.colors = colors;
    }
    toString() {
         return this.colors + ' ' + super.toString(); // 调用父类的toString()
    }
}

Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

上面代码中,constructor方法和toString方法之中,都出现了super关键字,它在这里表示父类的构造函数,用来新建父类的this对象。

子类必须在constructor方法中调用super方法,否则新建实例时会报错。如果子类没有定义constructor方法,这个方法会被默认添加,不管有没有显式定义,任何一个子类都有constructor方法。

ES5 的继承,实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))。ES6 的继承机制完全不同,实质是先创造父类的实例对象this(所以必须先调用super方法),然后再用子类的构造函数修改this