前言

对于灵活的js而言,继承相比于java等语言,继承实现方式可谓百花齐放。方式的多样就意味着知识点繁多,当然也是面试时绕不开的点。撇开ES6 class不谈,传统的继承方式你知道几种?每种实现原理是什么,优劣点能谈谈吗。这里就结合具体例子,按照渐进式的思路来看看继承的发展。

准备

谈到js继承之前先回顾下js 实例化对象的实现方式。

构造函数是指可以通过new 来实例化对象的函数,目的就是为了复用,避免每次都手动声明对象实例。

new 简单实现如下:

function my_new(func){
    var obj = {}
    obj._proto_ = func.prototype // 修改原型链指向,拼接至func原型链
    func.call(obj) // 实例属性赋值
    return obj
}

由上可以看出,通过构造函数调用,可以将实例属性赋值到目标对象上。
如此可以推想,子类中调用父类构造函数同样可以达到继承的目的。
这就提供了js继承的一种思路,即通过构造函数调用。

至于原型属性,就是通过修改原型指向,来实现原型属性的共享。
那么继承时同样也可以通过该方式进行。

总结

基于构造函数和原型链两种特性,结合js语言的灵活性。
继承的实现方式虽然繁多万变也不离其宗

继承的n种方式

原型式继承

定义:这种继承借助原型并基于已有的对象创建新对象,同时还不用创建自定义类型的方式称为原型式继承。

直接看代码更清晰:

function createObj(o) {
  function F() { }
  F.prototype = o;
  return new F();
}
var parent = {
  name: 'trigkit4',
  arr: ['brother', 'sister', 'baba']
};
var child1 = createObj(parent);

该方式表面上看基于对象创建,不需要构造函数(当然实际构造函数被封装起来罢了)。只借助了原型对象,所以名称为原型式继承。

缺点
比较明显优良者

  • 无法复用该继承,每个子类的实例,都要走完整的createObj流程。
  • 对于子类对象
    因为构造函数封装createObj中,对其而言,没有构造函数。由此造成无法初始化时传参。
    补充:其中 createObj 就是我们ES6中常用的Object.create(),不过Object.create进行了完善,允许额外参数来完善了。

解决思路
既然提到没有构造函数导致了问题,那么大胆猜测,更进一步就是涉及了构造函数的原型链继承了。

原型链式继承

定义:为了让子类继承父类的属性(也包括方法),首先需要定义一个构造函数。然后,将父类的新实例赋值给构造函数的原型。

function Parent() {
  this.name = 'mike';
}
function Child() {
  this.age = 12;
}
Child.prototype = new Parent();
child.prototype.contructor = child // 原型属性被覆盖,所以要修正回来。
var child1 = new Child();

也就是直接修改子类的原型对象指父构造函数的实例,这样把父类的实例属性和原型属性都挂到自己原型链上。

缺点

  • Child.prototype = new Parent() ,那么子函数自身的原型属性就被覆盖了,如果需要就要在后面补充。
  • 子对象实例化时,无法向父类构造函数传递参数。
    例如在new Child()执行的时候,想要去覆盖name,只能在Child.prototype = new Parent()时。 是我们在new Child()的时候统一传参初始化是更常规需求。

解决思路
如何在子类初始化时,调用父类构造函数。结合前面的基础,答案也呼之欲出。

借用构造函数(类式继承)

类式继承:是在子类型构造函数的内部调用超类型的构造函数。

思路比较清晰,由问题驱动。
既然原型链式子类不能向父类传参的问题,那么在子类初始化是调用父类不就满足目的了。

示例如下:

function Parent(age) {
  this.name = ['mike', 'jack', 'smith'];
  this.age = age;
}
Parent.prototype.run = function () {
  return this.name + ' are both' + this.age;
};
function Child(age) {
  // 调用父类
  Parent.call(this, age);
}
var child1 = new Child(21);

这样满足了初始化时传参的需求,但是问题也比较明显。

child1.run //undefined

问题

  • 父类原型属性丢失
    父类初始化只继承了示例属性,原型属性在子类的原型链上丢失

解决思路
丢失的原因在于原型链没有修改指向,那么修改下指向不就完了。

组合继承

定义:使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承

示例:

function Parent(age) {
  this.name = ['mike', 'jack', 'smith'];
  this.age = age;
}
Parent.prototype.run = function () {
  return this.name + ' are both' + this.age;
};
function Child(age) {
  // 调用父类构造函数
  Parent.call(this, age);
}
Child.prototype = new Parent();//原型属性继承
Child.prototype.contructor = Child
var child1 = new Child(21);

这样问题就避免了:

child1.run() // "mike,jack,smith are both21"

问题
功能满足之后,就该关注性能了。这种继承方式问题在于父类构造函数执行了两次。
分别是:

function Child(age) {
  // 调用父类构造函数,第二次
  Parent.call(this, age);
}
Child.prototype = new Parent();//修改原型链指向,第一次

解决思路
解决自然是取消一次构造函数调用,要取消自然要分析这两次执行,功能上是否有重复。
第一次同样继承了实例和原型属性,第二次执行同样继承了父类的实例属性。
因此第二次满足对父类传参的不可获取性,因此只能思考能否第一次不调用父类构造函数,只继承原型属性。
答案自然是能,前面原型式继承就是这个思路。

寄生组合式继承

顾名思义,寄生指的是将继承原型属性的方法封装在特定方法中,组合的是将构造函数继承组合起来,补充原型式继承的不足。

饶了点,直接看:

function createObj(o) {
  function F() { }
  F.prototype = o;
  return new F();
}
//继承原型属性 即原型式继承
function create(parent, child) {
  var f = createObj(parent.prototype);//获取原型对象
  child.prototype = f
  child.prototype.constructor = child;//增强对象原型,即保持原有constructor指向
}

function Parent(name) {
  this.name = name;
  this.arr = ['brother', 'sister', 'parents'];
}
Parent.prototype.run = function () {
  return this.name;
};
function Child(name, age) {
  // 示例属性
  Parent.call(this, name);
  this.age = age;
}
// 原型属性继承寄生于该方法中
create(Parent.prototype,Child);
var child1 = new Child('trigkit4', 21);

这样沿着发现问题解决问题的思路直到相对完善的继承方式。至于ES的方式本篇就不涉及了。

结束语

唯有厚积,才能薄发,想要心仪的offer,就得准备充裕,夯实基础,切忌似是而非,道理我都懂就是答得不完全,这样跟不懂差别也不太大。不算新的日子里立个flag,每周三个知识点回顾。要去相信,你若盛开蝴蝶自来。

前端面试基础回顾之深入JS继承的更多相关文章

  1. 前端读者 | 前端面试基础手册(HTML+CSS)

    本文来自@羯瑞:希望前端面试基础手册能帮助要找工作的前端小伙伴~~ HTML 前端需要注意哪些SEO? 合理的title.description.keywords:搜索对着三项的权重逐个减小,titl ...

  2. 前端面试基础题:Ajax原理

    Ajax 的原理简单来说是在⽤户和服务器之间加了—个中间层( AJAX 引擎),通过XmlHttpRequest 对象来向服务器发异步请求,从服务器获得数据,然后⽤ javascrip t 来操作 D ...

  3. 前端面试题目汇总摘录(JS 基础篇)

    JS 基础 JavaScript 的 typeof 返回那些数据类型 object number function boolean undefined string typeof null; // o ...

  4. web前端开发必懂之一:JS继承和继承基础总结

    首先,推荐一篇博客豪情的博客JS提高: http://www.cnblogs.com/jikey/p/3604459.html ,里面的链接全是精华, 一般人我不告诉他; 我们会先从JS的基本的设计模 ...

  5. 前端面试题目汇总摘录(JS 基础篇 —— 2018.11.02更新)

    温故而知新,保持空杯心态 JS 基础 JavaScript 的 typeof 返回那些数据类型 object number function boolean undefined string type ...

  6. 2019.7月-前端面试总结(H5+C3+JS+ES6+Vue+浏览器)

    第二次面试 HTML HTML5中的新标签,举例一下 canvas绘画,本地离线存储localStorage,sessionStorage,video和audio元素,语义化元素,表单类型(date, ...

  7. 前端面试基础-html篇之H5新特性

    h5的新特性(目前个人所了解)如下 语义化标签 表单新特性 视频(video)和音频(audio) canvas画布 svg绘图 地理定位 为鼠标提供的拖放API webworker (重点)Stor ...

  8. 前端面试基础-html篇之CSS3新特性

    CSS3的新特性(个人总结)如下 过度(transiton) 动画(animation) 形状转换 transform:适用于2D或3D转换的元素 transform-origin:转换元素的位置(围 ...

  9. 【原创】分布式之数据库和缓存双写一致性方案解析(三) 前端面试送命题(二)-callback,promise,generator,async-await JS的进阶技巧 前端面试送命题(一)-JS三座大山 Nodejs的运行原理-科普篇 优化设计提高sql类数据库的性能 简单理解token机制

    [原创]分布式之数据库和缓存双写一致性方案解析(三)   正文 博主本来觉得,<分布式之数据库和缓存双写一致性方案解析>,一文已经十分清晰.然而这一两天,有人在微信上私聊我,觉得应该要采用 ...

随机推荐

  1. Top 10 open source projects of 2015

    Top 10 open source projects of 2015 Posted 15 Dec 2015Jen Wike Huger (Red Hat)Feed 188 up 31 comment ...

  2. LA 4973 Ardenia (3D Geometry + Simulation)

    ACM-ICPC Live Archive 三维几何,题意是要求求出两条空间线段的距离.题目难度在于要求用有理数的形式输出,这就要求写一个有理数类了. 开始的时候写出来的有理数类就各种疯狂乱套,TLE ...

  3. laravel 中使用tinker注入数据到数据库

  4. Java 简单校验框架

    数据校验框架现状 在我们的方法入口后面,难免会有如下样子的代码: result.setSuccess(false); if (StringUtils.isBlank(bizOrder.getThird ...

  5. 求解最长回文串 manachar算法

    转载:http://blog.sina.com.cn/s/blog_70811e1a01014esn.html ;i<len;++i){ if(mx>i) p[i]=min(p[*id-i ...

  6. POJ 1182食物链、

    应用挑战程序设计那本书上的话: 对于每只动物i创建3个元素i—A,i—B,i—C,并用3xN个元素建立并查集.这个并查集维护如下信息: (1)i—x表示“i属于种类x” (2)并查集里的每一个组表示组 ...

  7. Lifecycle mapping "org.eclipse.m2e.jdt.JarLifecycleMapping" Eclipse Maven报错

    eclipse 项目中报错Lifecycle mapping "org.eclipse.m2e.jdt.JarLifecycleMapping" is not available, ...

  8. Flex AIR应用换肤功能(Android和IOS)

    说明 换肤功能,即将整个应用的皮肤都进行更换,其实质,是动态加载swf文件的过程,而这些swf文件则有css文件编译而来. 关于换肤功能,在android和ios系统的实现方式是不同的.主要原因,是因 ...

  9. Django入门10--admin增强

  10. windows常用命令行命令

    https://blog.csdn.net/qq_32451373/article/details/77743869 打开"运行"对话框(Win+R),输入cmd,打开控制台命令窗 ...