继承(inheritance)是面向对象软件技术当中的一个概念。
如果一个类别B“继承自”另一个类别A,就把这个B称为“A的子类”,而把A称为“B的父类别”也可以称“A是B的超类”
在js中可以用extend来去实现

1、js中常见的继承方式

  • 原型链继承
  • 构造函数继承(借助call)
  • 组合继承
  • 原型式继承
  • 寄生式继承
  • 寄生组合式继承

2、原型链继承

这个是用extend实现的继承。可以看出new的两个对象,彼此之间的值,没有任何的影响

class Car {constructor(color, speed) {this.color = colorthis.speed = [1, 2, 3, 4]// ...}
}
// // 货车
class Truck extends Car {constructor(color, speed) {super(color, speed)this.Container = true // 货箱}
}
let dadi = new Truck('白色')
let heid = new Truck('红色')
dadi.speed.push(5)
console.log(dadi.speed)
console.log(heid.speed)console.log(Truck.prototype)

输出:

然而采用原型链实现继承的话,就是将父类直接赋值给子类的prototype对象上

function Parent() {this.name = 'parent1';this.play = [1, 2, 3]
}
function Child() {this.type = 'child2';
}
Child.prototype = new Parent();
var s1 = new Child();
var s2 = new Child();
s1.name = 's1';
s1.play.push(4);
console.log(s1.play);
console.log(s2.play);console.log(s1.name);
console.log(s2.name);


这里其实可以看出来,当同时new出两个对象。
其中一个修改了play,另外一个对象的play同时也发生改变了。
这个是因为采用直接赋值的方式,相当于浅拷贝
父类中若是存在引用类型的话,只能复制对应的地址,因此修改的时候才会发生改变。
这里当修改name的时候,不发生改变。因为name是字符串,是原始数据类型。相当于又复制了一个出来,互不影响。
**采用extend实现的继承,就不存在这个问题。**因此这个方法不是最优的。

3、构造函数继承

借助call,Parent.call(this)改变Parent中的this指向。使得Child可以访问到Parent中的属性。

function Parent() {this.name = 'parent1';this.play = [1, 2, 3]
}Parent.prototype.getName = function () {return this.name;
}function Child() {Parent.call(this);this.type = 'child'
}let child1 = new Child();
let child2 = new Child();
child1.play.push(4);
console.log(child1.play);  // 1,2,3,4
console.log(child2.play);  // 1,2,3
console.log(child1.getName())// 报错:child1.getName is not a function

相比第一种原型链继承方式,父类的引用属性不会被共享,优化了第一种继承方式的弊端,但是只能继承父类的实例属性和方法,不能继承原型属性或者方法。
为什么不能原型的属性以及方法呢?因为这里的原型指向已经丢失了

在Parent构造函数中输出this,可以发现这里的prototype指向的是Object。

4、组合继承

集合了上面两种方法

function Parent3 () {this.name = 'parent3';this.play = [1, 2, 3];
}Parent3.prototype.getName = function () {return this.name;
}
function Child3() {// 第二次调用 Parent3()Parent3.call(this);this.type = 'child3';
}// 第一次调用 Parent3()
Child3.prototype = new Parent3();
// 手动挂上构造器,指向自己的构造函数
Child3.prototype.constructor = Child3;
var s3 = new Child3();
var s4 = new Child3();
s3.play.push(4);
console.log(s3.play, s4.play);  // 不互相影响
console.log(s3.getName()); // 正常输出'parent3'
console.log(s4.getName()); // 正常输出'parent3'

方式一和方式二的问题都解决了,但是这里Parent执行两次,造成了资源的浪费。

原型式继承(借助Object.create方法)

let parent4 = {name: "parent4",friends: ["p1", "p2", "p3"],getName: function() {return this.name;}};let person4 = Object.create(parent4);person4.name = "tom";person4.friends.push("jerry");let person5 = Object.create(parent4);person5.friends.push("lucy");console.log(person4); // tomconsole.log(person4.name === person4.getName()); // trueconsole.log(person5.name); // parent4console.log(person4.friends); // ["p1", "p2", "p3","jerry","lucy"]console.log(person5.friends); // ["p1", "p2", "p3","jerry","lucy"]

Object.create方法实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能。出现的问题其实是跟原型链直接复制出现的错误是一样的。

寄生式继承

缺点跟原型式继承一样。

let parent5 = {name: "parent5",friends: ["p1", "p2", "p3"],getName: function () {return this.name;}};function clone(original) {let clone = Object.create(original);clone.getFriends = function () {return this.friends;};return clone;}let person5 = clone(parent5);let person6 = clone(parent5);console.log(person5); // parent5console.log(person5.getFriends()); // ["p1", "p2", "p3"]person5.friends.push(2);console.log(person6.getFriends());

寄生组合式继承

是目前来说最优的继承方式:

function clone (parent, child) {// 这里改用 Object.create 就可以减少组合继承中多进行一次构造的过程child.prototype = Object.create(parent.prototype);child.prototype.constructor = child;
}function Parent6() {this.name = 'parent6';this.play = [1, 2, 3];
}
Parent6.prototype.getName = function () {return this.name;
}
function Child6() {Parent6.call(this);this.friends = 'child5';
}clone(Parent6, Child6);Child6.prototype.getFriends = function () {return this.friends;
}
let person6 = new Child6();
console.log(person6); //{friends:"child5",name:"child5",play:[1,2,3],__proto__:Parent6}
console.log(person6.getName()); // parent6
console.log(person6.getFriends()); // child5

总结

  • 原型链继承:new父类,并赋值给子类的原型属性

    • 缺点:new出来的对象,其中一个修改引用数据类型,另外一个会跟着变化。 因为两个实例使用的是同一个原型对象,内存空间是共享的。
  • 构造函数继承:借助call调用parent函数。
    • 缺点:相较于第一种原型链继承方式,父类的引用属性不会被共享。
  • 组合继承:组合了前两种方式
    • 缺点:person执行了两次,造成了多构造一次的性能开销。
  • 原型式继承:借助Object.create方式实现普通对象的继承
    • 缺点: 因为Object.create方法实现的是浅拷贝,多个实例的引用类型属性指向相同的内存,存在篡改的可能。
  • 寄生式继承。缺点跟原型式继承一样。
  • 寄生组合式继承 :采用Object.create+call方式(最优)

【前端面试之JS】js如何实现继承相关推荐

  1. 前端面试知识点大全——JS篇(三)

    总纲:前端面试知识点大全 目录 1.变量声明提升 2.冒泡机制 3.attribute 和 property 4.document load 和 document DOMContentLoaded 5 ...

  2. js 数组移除_2020前端面试--常见的js面试题

    (答案持续更新...) 1.简述同步和异步的区别 js是一门单线程语言,所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务 ...

  3. 前端面试送命题-JS三座大山

    前言 本篇文章比较适合3年以上的前端工作者,JS三座大山分别指:原型与原型链,作用域及闭包,异步和单线程. 原型与原型链 说到原型,就不得不提一下构造函数,首先我们看下面一个简单的例子: functi ...

  4. 阿里巴巴Web前端面试的一道JS题目,求解答!!!

    题目大概是这种: function outer(){return inner;var inner = "a";function inner(){};inner = 9; } ale ...

  5. 【前端面试必读】js排序的几种方法

    1.冒泡排序 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 针对所有的元素重复以上的步骤, ...

  6. 【持续..】WEB前端面试知识梳理 - CSS部分

    传送门: WEB前端面试知识梳理 - CSS部分 WEB前端面试知识梳理 - JS部分 最近在看大厂的一些面试题,发现很多问题都是平时没有在意的,很多知识都是知道一点但又很模糊说不出个所以然来,反思自 ...

  7. 华为js面试题_四面腾讯与华为,大厂前端面试真BT!

    今年算是经历颇多的一年了,腾讯和华为都走了几趟(一共面试了四个部门),拿了两个offer.(开心.png),但还是挂了两次,有点遗憾. 面试题总结 面试完之后,赶紧总结了一波,前端这个岗位,这两家大厂 ...

  8. 前端面试宝典 html css js ajax es6

    面试宝典 一.HTML和CSS 1 1. 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? 1 2. 每个HTML文件里开头都有个很重要的东西,Doctype,知道这是干什么的吗? 1 3. ...

  9. 前端面试 - JS总结(1) - 基础 (数据类型, 事件与函数, 原型链)

    你不走出舒适圈,又怎么知道自己多坚强?! 前端面试 - JS总结(1) - 基础 (数据类型, 事件与函数, 原型链) 前端面试 - JS总结(2) - ES6 (let, 箭头函数, this) 前 ...

最新文章

  1. Python入门100题 | 第029题
  2. 数据结构与算法笔记(十四)—— 二叉树
  3. #翻译# 关于 Java 性能方面的 9 个谬论
  4. 【python】入门指南:控制语句
  5. 有关 iOS 的开发证书、应用标识、设备标识、配置文件以及密钥 #DF...
  6. SAP Cloud for Customer用ABSL消费Restful Mashup API
  7. 两个有序链表序列的交集
  8. es6 Class 表达式
  9. 二十三种设计模式[6] - 适配器模式(Adapter Pattern)
  10. Linux云用户,Linux 用户与权限
  11. IDEA 配置SVN ,SVN安装后没有svn.exe
  12. 再战高端智能电动化,错失先机的长安能靠华为“翻盘”?
  13. 蓝牙beacon入门教程
  14. SAP中决定销售订单出现在MRP运算中的因素
  15. 【面试总结】网易2019秋招一站式面试总结(等offer中……)
  16. (十六)Hibernate中的延迟加载
  17. Android 优化开机启动
  18. JS 判断浏览器客户端类型(ipad,iphone,android)
  19. SQL:开窗函数(窗口函数)
  20. win10如何更改计算机用户名,怎么更改账户用户名,教你win10系统更改账户用户名称教程...

热门文章

  1. [Luogu 2265]路边的水沟
  2. 线性回归模型(7大模型)
  3. 解决vue-cli默认使用yarn或者npm的问题
  4. 小白玩Ubuntu——一脸懵逼到爱不释手
  5. imopen和bwmorph_形态学笔记
  6. 【python】win10 安装 IPython
  7. HUAWEI华为笔记本电脑MateBook 14 2021款i7独显触屏(KLVD-WFE9)原装出厂Windows10系统恢复原厂OEM系统20H2
  8. 美术绘画水平和3D建模的关系
  9. 图像深度和图像内存的计算
  10. duilib 模仿网易云音乐