0
点赞
收藏
分享

微信扫一扫

面试官问:JS的继承

写于2019220日,现在发到公众号声明原创,之前被《前端大全》公众号等转载阅读量超1w+,知乎等累计阅读量超过1w+。

导读:文章主要通过ES6的extends,结合ES5的寄生组合继承,图文并茂的讲述JS的继承,最后还推荐了一些书籍的继承的章节,旨在让读者掌握JS的继承。

用过 ​React​​的读者知道,经常用 ​extends​​继承 ​React.Component​。

面试官问:JS的继承_构造器

点击这里查看 React github源码

面试官可以顺着这个问 ​JS​继承的相关问题,比如: ​ES6​的 ​class​继承用ES5如何实现。据说很多人答得不好。

构造函数、原型对象和实例之间的关系

要弄懂extends继承之前,先来复习一下构造函数、原型对象和实例之间的关系。代码表示:

面试官问:JS的继承_构造器_02

笔者画了一张图表示: 

面试官问:JS的继承_原型对象_03

​ES6extends​​ 继承做了什么操作

我们先看看这段包含静态方法的 ​ES6​继承代码:

面试官问:JS的继承_构造函数_04

其中这段代码里有两条原型链,不信看具体代码。

面试官问:JS的继承_原型对象_05

一图胜千言,笔者也画了一张图表示,如图所示:

面试官问:JS的继承_构造器_06

 结合代码和图可以知道。 ​ES6extends​ 继承,主要就是:

  • 1.把子类构造函数( ​Child​​)的原型( ​__proto__​​)指向了父类构造函数( ​Parent​),

  • 2.把子类实例 ​child​​的原型对象( ​Child.prototype​​) 的原型( ​__proto__​​)指向了父类 ​parent​​的原型对象( ​Parent.prototype​)。

这两点也就是图中用不同颜色标记的两条线。

  • 3.子类构造函数 ​Child​​继承了父类构造函数 ​Preant​​的里的属性。使用 ​super​​调用的( ​ES5​​则用 ​call​​或者 ​apply​调用传参)。也就是图中用不同颜色标记的两条线。

看过《JavaScript高级程序设计-第3版》 章节 ​6.3继承​​的读者应该知道,这 ​23小点​,正是寄生组合式继承,书中例子没有 ​1小点​​。 ​12小点​​都是相对于设置了 ​__proto__​​链接。那问题来了,什么可以设置了 ​__proto__​链接呢。

​new​​​、 ​​Object.create​​​和 ​​Object.setPrototypeOf​​​可以设置 ​​__proto__​

说明一下, ​__proto__​​这种写法是浏览器厂商自己的实现。再结合一下图和代码看一下的 ​new​​, ​new​出来的实例的proto指向构造函数的 ​prototype​​,这就是 ​new​做的事情。摘抄一下之前写过文章的一段。面试官问:能否模拟实现JS的new操作符,有兴趣的读者可以点击查看。

​new​​做了什么:

  1. 创建了一个全新的对象。
  2. 这个对象会被执行 [[Prototype]](也就是 __proto__)链接。
  3. 生成的新对象会绑定到函数调用的 this
  4. 通过 new创建的每个对象将最终被 [[Prototype]]链接到这个函数的 prototype对象上。
  5. 如果函数没有返回对象类型 Object(包含 FunctoinArrayDateRegExgError),那么 new表达式中的函数调用会自动返回这个新的对象。

​Object.create​​​ ​​ES5提供的​

Object.create(proto,[propertiesObject])​ 方法创建一个新对象,使用现有的对象来提供新创建的对象的proto。它接收两个参数,不过第二个可选参数是属性描述符(不常用,默认是 ​undefined​​)。对于不支持 ​ES5​​的浏览器, ​MDN​​上提供了 ​ployfill​方案。 MDN Object.create()

面试官问:JS的继承_构造器_07

​Object.setPrototypeOf​​​ ​​ES6提供的​

Object.setPrototypeOf​​ ​MDN

Object.setPrototypeOf()​​ 方法设置一个指定的对象的原型 ( 即, 内部 ​[[Prototype]]​​属性)到另一个对象或 ​null​​。 ​Object.setPrototypeOf(obj,prototype)

面试官问:JS的继承_构造函数_08

nodejs​源码就是利用这个实现继承的工具函数的。 nodejs utils inherits

面试官问:JS的继承_原型对象_09

​ES6​​​的 ​​extends​​​的 ​​ES5​​版本实现

知道了 ​ES6extends​​继承做了什么操作和设置 ​__proto__​​的知识点后,把上面 ​ES6​​例子的用 ​ES5​就比较容易实现了,也就是说实现寄生组合式继承,简版代码就是:

面试官问:JS的继承_原型对象_10

我们完全可以把上述 ​ES6的例子​​通过 ​babeljs​​转码成 ​ES5​来查看,更严谨的实现。

面试官问:JS的继承_构造函数_11

面试官问:JS的继承_构造器_12

面试官问:JS的继承_原型对象_13

面试官问:JS的继承_构造函数_14

如果对JS继承相关还是不太明白的读者,推荐阅读以下书籍的相关章节,可以自行找到相应的 ​pdf​版本。

推荐阅读JS继承相关的书籍章节

《JavaScript高级程序设计第3版》-第6章 面向对象的程序设计,6种继承的方案,分别是原型链继承、借用构造函数继承、组合继承、原型式继承、寄生式继承、寄生组合式继承。图灵社区本书地址,后文放出 ​github​​链接,里面包含这几种继承的代码 ​demo​。

《JavaScript面向对象编程第2版》-第6章 继承,12种继承的方案。1.原型链法(仿传统)、2.仅从原型继承法、3.临时构造器法、4.原型属性拷贝法、5.全属性拷贝法(即浅拷贝法)、6.深拷贝法、7.原型继承法、8.扩展与增强模式、9.多重继承法、10.寄生继承法、11.构造器借用法、12.构造器借用与属性拷贝法。

ES6标准入门-第21章class的继承

《深入理解 ​ES6​​》-第9章 ​JavaScript​中的类

《你不知道的 ​JavaScript​​-上卷》第6章 行为委托和附录A ​ES6中的class

总结

继承对于JS来说就是父类拥有的方法和属性、静态方法等,子类也要拥有。子类中可以利用原型链查找,也可以在子类调用父类,或者从父类拷贝一份到子类等方案。继承方法可以有很多,重点在于必须理解并熟 悉这些对象、原型以及构造器的工作方式,剩下的就简单了。寄生组合式继承是开发者使用比较多的。回顾寄生组合式继承。主要就是三点:

  • 1.子类构造函数的 ​__proto__​指向父类构造器,继承父类的静态方法。

  • 2.子类构造函数的 ​prototype​​的 ​__proto__​​指向父类构造器的 ​prototype​,继承父类的方法。

  • 3.子类构造器里调用父类构造器,继承父类的属性。行文到此,文章就基本写完了。文章代码和图片等资源放在这里github inhert和 ​demo​​展示 ​es6-extends​​,结合 ​consolesource​面板查看更佳。

读者发现有不妥或可改善之处,欢迎评论指出。另外觉得写得不错,可以点赞、评论、转发,也是对笔者的一种支持。

关于

作者:常以若川为名混迹于江湖。前端路上 | PPT爱好者 | 所知甚少,唯善学。

下图是公众号二维码:若川视野,一个可能比较有趣的前端开发类公众号,目前前端内容不多

面试官问:JS的继承_构造函数_15

举报

相关推荐

0 条评论