函数的继承

这篇博客深入探讨了JavaScript中几种常见的继承方式,包括构造函数的继承、原型链继承、组合继承及其优化。文中通过实例展示了每种继承方式的特点,如构造函数只能部分继承,原型链继承可能导致共享状态的问题,而组合继承解决了这个问题但会引发重复调用父构造函数的开销。作者还分析了优化策略及其存在的问题,并最终提出了一种更优的解决方案,强调了`Object.create`在继承中的作用和原理。

构造函数的继承

    function parent() {
        this.name = 1
        this.play = [3,4]
    }
    parent.prototype.say = function () {
        console.log('say')
    }
    function children() {
        parent.call(this)
        this.age = 2
    }
    console.log(new children().name)
    console.log(new children().say())

结果如图:这样的继承只可以继承父函数的自身属性,不可以继承原型链say属性,也就是说只可以部分继承

原型链继承

    function parent2() {
        this.name = 1
        this.play = [3,4]
    }
    parent2.prototype.say = function () {
        console.log('say')
    }
    function children2() {
        this.age = 2
    }
    children2.prototype = new parent2();
    console.log(new children2().name)
    console.log(new children2().say())
    var m1 = new children2()
    var m2 = new children2()
    m1.play.push(5)
    console.log(m1.play)
    console.log(m2.play)

结果如图:这样的继承虽然全部继承了,可以找到name和say属性,但是由于m2.__proto__ === m1.__proto__ 为true,都指向同一个原型链children2.prototype,所以当m1数组改变的时候,m2也跟着变

组合继承

    function parent2() {
        this.name = 1
        this.play = [3,4]
    }
    parent2.prototype.say = function () {
        console.log('say')
    }
    function children2() {
        parent2.call(this)
        this.age = 2
    }
    children2.prototype = new parent2();
    console.log(new children2().name)
    console.log(new children2().say())
    var m1 = new children2()
    var m2 = new children2()
    m1.play.push(5)
    console.log(m1.play)
    console.log(m2.play)

 

结果如下:上面代码就是组合上面两种继承,因为他是通过parent2.call(this)改变this继承的,m1和m2是new出来的两个不同的实例,这样即使m1的数组改变了,m2也不会变,但是这样子写parent2执行了两次

 

优化方式

    function parent2() {
        this.name = 1
        this.play = [3,4]
    }
    parent2.prototype.say = function () {
        console.log('say')
    }
    function children2() {
        parent2.call(this)
        this.age = 2
    }
    children2.prototype =  parent2.prototype;
    console.log(new children2().name)
    console.log(new children2().say())
    var m1 = new children2()
    var m2 = new children2()
    m1.play.push(5)
    console.log(m1.play)
    console.log(m2.play)

 

结果如下:可以看到m2.__proto__.constructor居然是parent2,按道理应该是children2的,因为m2.__proto__.constructor === children2.prototype.constructor,但是children2.prototype = parent2.prototype直接指向了parent2,所以m2.__proto__.constructor直接指向parent2,所以这个优化方式也有缺点

 

最终优化

    function parent2() {
        this.name = 1
        this.play = [3,4]
    }
    parent2.prototype.say = function () {
        console.log('say')
    }
    function children2() {
        parent2.call(this)
        this.age = 2
    }
    children2.prototype =  Object.create(parent2.prototype);
    children2.prototype.constructor = children2;
    console.log(new children2().name)
    console.log(new children2().say())
    var m1 = new children2()
    var m2 = new children2()
    m1.play.push(5)
    console.log(m1.play)
    console.log(m2.play)

结果如下:可以看到m2.__proto__.constructor是children2的

也许会有疑问为什么不直接如下代码这样,为什么要用Object.create?因为children2.prototype.constructor = children2,如下代码所以parent2.prototype.constructor = children2,这样就连parent2一起改了 

children2.prototype =  parent2.prototype;    
children2.prototype.constructor = children2;

然而children2.prototype =  Object.create(parent2.prototype)这行代码意味着children2.prototype._proto_ =  parent2.prototype,为什么呢?我们先来看看Object.create发生了什么,关键代码如下:

Object.create =  function (o) {
    var F = function () {};
    F.prototype = o;
    var newObj=new F();
    return newObj;
};
可以看到Object.create内部创建了一个新对象newObj,
默认情况下newObj.__proto__== F.prototype,
在本例中则重写了构造函数F的原型属性,
最终的原型关系链为newObj.__proto__== F.prototype == o

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__,所以最终版本是使用Object.create()来创建children2.prototype

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值