【JS档案揭秘】第三集 深入最底层探秘原型链

  关于这部分我看过大量的文章,数不胜数,包括阮一峰的继承三部曲,还有各种慕课的视频教程,网上无数继承方法的对比。也对很多概念存在长期错误的理解。今天做一个正确的总结,用来给原型链和继承这块知识画上句号,而从深度上来说,则是深入到堆内存中去一探究竟。

  

业务场景

  原型与原型链,从作用上来说,理解它在当前时代用处其实不大,因为ES6的普及,封装的class语法糖让人们很难想再去翻构造函数、原型链这些历史旧账。而且ES6的class除了不支持IE、黑莓这种复古浏览器,主流的现代浏览器都已得到了全面支持。而且再不济,要去兼容IE,也有babel来帮我们转译class语法,用不着去手写prototype,__proto__等关系。

  所以研究原型链的作用到底在哪儿呢?我认为它的作用在于从知识层面帮我们看清楚extends和super的本质。至于在业务层面,它并没有任何作用。

 

原理阐述

  首先,prototype属性是函数独有的,它指向一个堆内存中的对象,这个对象就叫原型对象。

  原型对象有很多个,每个构造函数都对应着一个原型对象;

  当函数作为构造函数使用,也就是实例化时,它会把这个原型对象的指针命名为__proto__,并强加到实例化对象中。这样一来,某个构造函数的prototype属性和通过这个构造函数实例化的所有对象的__proto__属性,都指向某一个原型对象。

  所谓原型链,就是将堆内存中不同的原型对象串起来,那怎么让这些分散在不同堆内存的,毫不相关的原型对象串起来呢?

  答案就是:引用。因为原型对象也是一个对象,它也有__proto__属性,所以,只要让一个某个原型对象的__proto__属性,指向另外一个原型对象。这样就在毫不相关的两个原型对象之间打通了关系。换言之,这样就实现了继承。

 

画图理解

  

  在上图中,原型对象1和原型对象2的__proto__属性都默认指向根原型对象。如果要使原型对象1继承原型对象2,则把原型对象1的默认指向根原型对象的__proto__属性,改为指向原型对象2。(灰色箭头转为蓝色虚线箭头)

  当然,为了不使这副图变得混乱,我没有把一个东西画出来,那就是原型对象的constructor属性指向其构造函数,也就是说,一个构造函数可以通过prototype属性找到其对应的原型对象,一个原型对象也可以通过自身的constructor属性找到其对应的构造函数,这也侧面印证了:在堆内存中的原型对象是很多的。

  

 

  

ES6的语法糖分析

  在ES6中,定义了类,extends和super。它们的本来面目如下:

  假设有类A和类B,定义class A extends B。且在类A的constructor构造函数中调用了super函数,实现了类A对类B的继承。

  ES6中的类就是构造函数,extends在这里表示A.prototype.__proto__ = B.prototype。而super则是ES5中的A.call(this,args)。args表示A中传入的实例化参数。

  关于super,你写不写都可以。如果你要在子类定义一个constructor,且constructor里面要用到this,那么super就是必写的,且需要写在this出现之前;如果不是我刚刚提到的这种情况,那么可以不写;

 

代码验证

  我们敲出一系列代码来验证我的总结:

  

  (验证一:原型链图,通过)

 

   

 

(验证2:ES6语法糖,通过)

 

posted @ 2019-08-19 13:59  陌上兮月  阅读(...)  评论(...编辑  收藏