JS 继承

本文大部分参考自https://www.cnblogs.com/chengzp/p/prototype.html

原型链

简单理解就是原型组成的链,实例的__proto__是它的原型,而原型也是一个对象,也有__proto__属性,原型的__proto__又是原型的原型,就这样可以一直通过__proto__想上找,直到最后的null,这就是原型链,当向上找找到Object的原型(null)的时候,这条原型链就算到头了。

 

实例声明的过程:生成构造函数,这个构造函数会自带一个propertype的属性指向原型对象,这个原型对象自带一个constructor属性指向构造函数本生,然后利用构造函数new产生一个实例,实例自带一个__proto__属性指向原型对象。

只有函数有prototype、对象实例有__proto__属性,所以原型对象也具有__proto__属性,会指向对应的原型对象。

 

另:instanceof原理

instanceof用于判断实例的__proto__和生成该实例构造函数的prototype是不是引用同一个地址。

举几个例子:

这里定义了一个构造函数,然后通过构造函数定义了一个p实例。

参考上图和文字的理解,实例的__proto__和构造函数的prototype它们的引用是相同的。

第一个console中p实例的__proto__和它的构造函数Person的prototype是相同的

第二个console中Person的__proto__,对应的就是Person的构造函数的prototype也就是Function的prototype

第三个console中Person的prototype对应的简单对象即Object对象,而这个对象的构造函数对应的是Object,所以将Person.prototype看作对象a,那么对象a的__proto__也就是对象a的构造函数的prototype,即Object.prototype

第四个console中Object的prototypeObject

console.log(Object.__proto__ === Function.prototype)//true  由于object是构造函数,道理同上

图片取自https://segmentfault.com/q/1010000013876889/a-1020000013879132中的一个回答

看完后茅塞顿开感觉清晰了很多

 

继承

可以看到Sub中并没有声明toString方法,但是仍然可以运行,因为,在sub实例中找不到方法,会在原型链中找,而Sub的原型对象是object对象,而toString又是object原型中的方法,所以可以找到。(类似于作用域链一样)

 

原型链继承

关键:子类型的原型为夫类型的一个实例对象

Sub.prototype = new Super();  

代码和内存图

图解释:首先声明了一个Super方法,于是有了Super函数对象和Super原型对象,然后声明sup实例,实例中的proto会指向它的构造函数Super的原型对象;同理,然后声明了一个Sub方法,于是有了Sub函数对象和Sub原型对象,然后声明sub实例,实例中的proto会指向它的构造函数Sub的原型对象。

现在想让sub实例能访问到super中的name和age属性。思考:直接通过sub.age会出现undefined,因为sub对象本生没有该属性,而且搜索原型链也找不到这个属性(也就是在Sub的prototype中找不到,在prototype的proto也找不到),那么既然是继承,sub想访问super中所有属性,通过访问顺序(找sub,然后找Sub.prototype),所以可以将Sub.prototype指向super的实例,这样sub中虽然找不到,但在prototype中可以访问到,达到了继承的效果。

上面的思考为看了原型链后的想法。以下为具体的实现形式

原型式继承:原理,让子类构造函数的prototype指向父类的prototype,即共享同一个原型对象咯

缺点:子类只能继承父类原型对象上的成员,子类成员都共享父类的原型对象成员,导致修改一个子类,其他子类也会改变

注,这里的修改子类,是指修改了子类共享了那个原型对象

例:

 

原型链继承:原理,让子类构造函数的prototype指向父类的实例

解决了原型式继承中的无法继承父类实例中的成员的问题

同上,子类成员的修改会互相影响,同样的,由于Father对象对于Son来说是它的原型对象,所以除了基础类型无法修改外,对其他类型的修改都会影响到其他子类成员

注:切记,对原型对象的成员无法修改值,除了引用类型的成员

 

借用构造函数继承

原理:使用call和apply借用其他构造函数的成员

解决了子类成员相互影响的问题,但子类成员无法访问父类的原型对象。

在s = new Son()构造函数时,构造函数内部this的值指向s,所以Father.call(this)相当于Father.call(s),由于call方法可以改变函数的作用域,也就相当于s.Father(),于是当s调用Father时,会在堆中生成新的对象,并赋值,所以子类都是相互独立的,不会互相影响。

缺点:假如父类构造函数中有方法,那么内存消耗会很大,因为每个实例都会拷贝相同的方法;无法访问父类的原型对象。

 

组合继承

原理:借用构造函数+原型链继承

解决了借用构造函数继承时无法访问父类原型对象的问题;

缺陷:s1对原型对象的修改会影响到s1的访问,因为按照原型式继承,他们是共享Father对象的原型成员。(尽管子类的其他成员都是相互独立的)

还有个问题,就是这种继承会调用两次父类构造函数,一次是

二次是,如下图:

寄生组合式

原理:借用构造函数+原型链继承,并解决组合模式中多次调用父类构造函数的问题。

这个方法只是进行了一个简单的赋值操作,生成一个父类原型,并将该原型的constructor指向son,最后将该原型传给son的prototype

 

继承大概就这些吧,等学到后面再回来补充。

 

 

 

 

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值