es5继承

105 阅读3分钟

1,构造函数继承

   function Parent1() {
        this.name = "parent1"
    }
    Parent1.prototype.say = function () {
        console.log("hi")
    }

    function Child1() {
        Parent1.call(this);//构造函数实现继承的关键
        this.type = "child1"
    }
    console.log(new Child1().say)  //取不到say方法 报错:Uncaught TypeError: (intermediate value).say is not a function

构造函数继承的实现方式:在子类型构造函数中调用父类型构造函数,并通过call修改this指向;

缺点:只能继承构造函数中的属性和方法,无法继承父构造函数原型对象的属性和方法,无复用性可言;

2,原型链继承

function Parent2() {
    this.name = "parent2"
    this.color = ["blue", "red"]
}
Parent2.prototype.say = function () {
    console.log("hi")
}

function Child2() {
    this.type = "child2"
}
Child2.prototype = new Parent2();//原型链继承实现的关键

let c1 = new Child2();
let c2 = new Child2();
c1.color.push("yellow")

console.log(c1.color, c2.color)  //["blue", "red", "yellow"], ["blue", "red", "yellow"],此时c1.color改变影响了c2.color

原型链继承实现的方式:子构造函数的原型对象等于父构造函数的实例
因为 new Child2().proto === Child2.prototype;
所以 new Child2().proto = new Parent2();
即子构造函数的原型链指向父构造函数的实例;

缺点:因为所有的子构造函数的原型链都指向父类构造函数的实例,如果父类构造函数的中的属性包含引用类型,这个属性会被所有的子类实例所共享(此时c1.color改变影响了c2.color),因此不推荐使用纯原型链的形式继承

3,组合式继承

function Parent3() {
    this.name = "parent3"
    this.color = ["blue", "red"]
}
Parent3.prototype.say = function () {
    console.log("hi")
}

function Child3() {
    Parent3.call(this);//组合继承实现的关键
    this.type = "child3"
}
Child3.prototype = new Parent3();//组合继承实现的关键

let c1 = new Child3();
let c2 = new Child3();
childTest1.color.push("yellow");
console.log(c1.color, c2.color, c1.say) //["blue", "red", "yellow"] ,["blue", "red"],
console.log(c1)  //结果如下图

组合继承后继同时承了父构造函数中父构造函数原型中的属性、方法,同时也保证了子类型构造函数属性的独立

组合继承的实现方式:在子类型构造函数中调用父类型构造函数+子构造函数的原型对象等于父构造函数实例

缺点:子类的实例在生成时,多次调用了父类构造函数,如下图

4,组合继承的优化1

function Parent4() {
    this.name = "parent4";
    this.color = ["blue", "red"]
}
Parent4.prototype.say = function () {
    console.log("hi")
}

function Child4() {
    Parent4.call(this);//组合继承实现的关键
    this.type = "child4"
}
Child4.prototype = Parent4.prototype;//组合继承优化

let c1 = new Child4();
let c2 = new Child4();
c1.color.push('yellow');
console.log(c1.color, c2.color) 

为了解决组合继承存在的问题,对组合继承进行优化;把父类构造函数的原型对象赋值给子构造函数的原型对象;此时实例.__proto__的值中只有say方法,没有再次调用父类构造函数自身中的属性和方法;

为了对比,打印下组合继承和优化后的组合继承的实例,如下图

优化后的组合继承实现方法:在子类型构造函数中调用父类型构造函数+子构造函数的原型对象等于父构造函数的原型对象;

缺点:虽然已经可以实现继承,但是此时打印实例的构造函数发现,实例的构造函数是Parent4,而不是Child4,因此需要对组合继承继续优化

//ƒ Parent4() {
//    this.name = "parent4";
//    this.color = ["blue", "red"]
//}

5.组合继承优化2

function Parent5() {
    this.name = "parent5";
    this.color = ["blue", "red"]
}
Parent5.prototype.say = function () {
    console.log("hi")
}

function Child5() {
    Parent5.call(this);//组合继承实现的关键
    this.type = "child5"
}
Child5.prototype = Parent5.prototype;//组合继承实现的关键
Child5.prototype.constructor = Child5;//组合继承优化

let c1 = new Child5();
let c2 = new Child5();
console.log(c1.constructor)  //Child5
console.log(Parent5.prototype.constructor) //Child5

通过优化,可以发现子类的实例化对象的构造函数指向已经正确,但是,此时打印父构造函数的原型对象的构造器,发现,构造器的指向也是Child5,还要继续优化

6,组合继承优化3

function Parent6() {
    this.name = "parent6";
    this.color = ["blue", "red"]
}
Parent6.prototype.say = function () {
    console.log("hi")
}

function Child6() {
    Parent6.call(this);//组合继承实现关键
    this.type = "child6"
}
Child6.prototype = Object.create(Parent6.prototype);//组合继承实现关键
Child6.prototype.constructor = Child6;//组合继承实现关键

let c1 = new Child6();
let c2 = new Child6();
c1.color.push('yellow');
console.log(c1.constructor)
console.log(Parent6.prototype.constructor)

优化内容:Object.create(a),可以创建一个原型对象为a的对象,通过Object.create(Parent6.prototype)可以拿到一个和父类原型对象值一样的新对象,在修改子类原型对象的构造器(constructor)时,不会影响到父类对象的构造函数。

致此,已经得到一个较完善的ES5继承~