【前端面试之JS】js如何实现继承
继承(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如何实现继承相关推荐
- 前端面试知识点大全——JS篇(三)
总纲:前端面试知识点大全 目录 1.变量声明提升 2.冒泡机制 3.attribute 和 property 4.document load 和 document DOMContentLoaded 5 ...
- js 数组移除_2020前端面试--常见的js面试题
(答案持续更新...) 1.简述同步和异步的区别 js是一门单线程语言,所谓"单线程",就是指一次只能完成一件任务.如果有多个任务,就必须排队,前面一个任务完成,再执行后面一个任务 ...
- 前端面试送命题-JS三座大山
前言 本篇文章比较适合3年以上的前端工作者,JS三座大山分别指:原型与原型链,作用域及闭包,异步和单线程. 原型与原型链 说到原型,就不得不提一下构造函数,首先我们看下面一个简单的例子: functi ...
- 阿里巴巴Web前端面试的一道JS题目,求解答!!!
题目大概是这种: function outer(){return inner;var inner = "a";function inner(){};inner = 9; } ale ...
- 【前端面试必读】js排序的几种方法
1.冒泡排序 比较相邻的元素.如果第一个比第二个大,就交换他们两个. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 针对所有的元素重复以上的步骤, ...
- 【持续..】WEB前端面试知识梳理 - CSS部分
传送门: WEB前端面试知识梳理 - CSS部分 WEB前端面试知识梳理 - JS部分 最近在看大厂的一些面试题,发现很多问题都是平时没有在意的,很多知识都是知道一点但又很模糊说不出个所以然来,反思自 ...
- 华为js面试题_四面腾讯与华为,大厂前端面试真BT!
今年算是经历颇多的一年了,腾讯和华为都走了几趟(一共面试了四个部门),拿了两个offer.(开心.png),但还是挂了两次,有点遗憾. 面试题总结 面试完之后,赶紧总结了一波,前端这个岗位,这两家大厂 ...
- 前端面试宝典 html css js ajax es6
面试宝典 一.HTML和CSS 1 1. 你做的页面在哪些流览器测试过?这些浏览器的内核分别是什么? 1 2. 每个HTML文件里开头都有个很重要的东西,Doctype,知道这是干什么的吗? 1 3. ...
- 前端面试 - JS总结(1) - 基础 (数据类型, 事件与函数, 原型链)
你不走出舒适圈,又怎么知道自己多坚强?! 前端面试 - JS总结(1) - 基础 (数据类型, 事件与函数, 原型链) 前端面试 - JS总结(2) - ES6 (let, 箭头函数, this) 前 ...
最新文章
- Python入门100题 | 第029题
- 数据结构与算法笔记(十四)—— 二叉树
- #翻译# 关于 Java 性能方面的 9 个谬论
- 【python】入门指南:控制语句
- 有关 iOS 的开发证书、应用标识、设备标识、配置文件以及密钥 #DF...
- SAP Cloud for Customer用ABSL消费Restful Mashup API
- 两个有序链表序列的交集
- es6 Class 表达式
- 二十三种设计模式[6] - 适配器模式(Adapter Pattern)
- Linux云用户,Linux 用户与权限
- IDEA 配置SVN ,SVN安装后没有svn.exe
- 再战高端智能电动化,错失先机的长安能靠华为“翻盘”?
- 蓝牙beacon入门教程
- SAP中决定销售订单出现在MRP运算中的因素
- 【面试总结】网易2019秋招一站式面试总结(等offer中……)
- (十六)Hibernate中的延迟加载
- Android 优化开机启动
- JS 判断浏览器客户端类型(ipad,iphone,android)
- SQL:开窗函数(窗口函数)
- win10如何更改计算机用户名,怎么更改账户用户名,教你win10系统更改账户用户名称教程...