“约见”面试官系列之常见面试题第三十八篇之js常见的继承方式(建议收藏)
1.原型链继承
核心: 将父类的实例作为子类的原型
将构造函数的原型设置为另一个构造函数的实例对象,这样就可以继承另一个原型对象的所有属性和方法,可以继续往上,最终形成原型链
父类
// 定义一个动物类
function Animal (name) {// 属性this.name = name || 'Animal';// 实例方法this.sleep = function(){console.log(this.name + '正在睡觉!');}
}
// 原型方法
Animal.prototype.eat = function(food) {console.log(this.name + '正在吃:' + food);
};
function Cat(){
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true
console.log(cat instanceof Cat); //true
问题
1.来自原型对象的所有属性被所有实例共享
2.创建子类实例时,无法向父类构造函数传参
2.构造函数继承
使用父类的构造函数来增强子类实例,等于是复制父类的实例属性给子类(没用到原型)
function SuperType() {this.colors = ["red", "blue", "green"];
}function SubType() {//继承SuperTypeSuperType.call(this);
}var instance1 = new SubType();
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
var instance2 = new SubType();
alert(instance2.colors); //"red,blue,green"
特点:
解决了1中,子类实例共享父类引用属性的问题
创建子类实例时,可以向父类传递参数
可以实现多继承(call多个父类对象)
缺点:
实例并不是父类的实例,只是子类的实例
只能继承父类的实例属性和方法,不能继承原型属性/方法
无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
3.组合继承
将原型链和借用构造函数的技术组合到一块。使用原型链实现对原型属性和方法的继承,而通过构造函数来实现对实例属性的继承。
function SuperType(name) {this.name = name;this.colors = ["red", "blue", "green"];
}SuperType.prototype.sayName = function() {alert(this.name);
}function SubType(name, age) {// 继承属性SuperType.call(this, name);this.age = age;
}// 继承方法
SubType.prototype = new SuperType();
SubType.prototype.constructor = SubType;
SubType.prototype.sayAge = function() {alert(this.age);
};var instance1 = new SubType("Nicholas", 29);
instance1.colors.push("black");
alert(instance1.colors); //"red,blue,green,black"
instance1.sayName(); //"Nicholas";
instance1.sayAge(); //29
var instance2 = new SubType("Greg", 27);
alert(instance2.colors); //"red,blue,green"
instance2.sayName(); //"Greg";
instance2.sayAge(); //27
将SubType的原型指定为SuperType的一个实例,大致步骤和原型链继承类似,只是多了在SubType中借调SuperType的过程。
实例属性定义在构造函数中,而方法则定义在构造函数的新原型中,同时将新原型的constructor指向构造函数。
可以通过instanceof和isPrototypeOf()来识别基于组合继承创建的对象。
避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为JS中最常用的继承模式。
4.原型式继承
不自定义类型的情况下,临时创建一个构造函数,借助已有的对象作为临时构造函数的原型,然后在此基础实例化对象,并返回。
function object(o){function F(){}F.prototype = o;return new F();
}
本质上是object()对传入其中的对象执行了一次浅复制
var person = {name: "Nicholas",friends: ["Shelby", "Court", "Van"]
};var anotherPerson = object(person);
anotherPerson.name = "Greg";
anotherPerson.friends.push("Rob");var yetAnotherPerson = object(person);
yetAnotherPerson.name = "Linda";
yetAnotherPerson.friends.push("Barbie");alert(person.friends); //"Shelby,Court,Van,Rob,Barbie"
原型的引用类型属性会在各实例之间共享。
当只想单纯地让一个对象与另一个对象保持类似的情况下,原型式继承是完全可以胜任的。
5.寄生组合式继承
组合继承是JS中最常用的继承模式,但其实它也有不足,组合继承无论什么情况下都会调用两次超类型的构造函数,并且创建的每个实例中都要屏蔽超类型对象的所有实例属性。
寄生组合式继承就解决了上述问题,被认为是最理想的继承范式。
function object(o) {function F(){}F.prototype = o;return new F();
}function inheritPrototype(superType, subType) {var prototype = object(superType.prototype);prototype.constructor = subType;subType.prototype = prototype;
}function SuperType(name) {this.name = name;this.colors = ["red", "blue", "green"];
}SuperType.prototype.sayName = function() {alert(this.name);
};function SubType(name, age) {SuperType.call(this, name);this.age = age;
}inheritPrototype(SuperType, SubType); // 这一句,替代了组合继承中的SubType.prototype = new SuperType()SubType.prototype.sayAge = function() {alert(this.age);
};
不必为了指定子类型的原型而调用超类型的构造函数,我们需要的只不过是超类型原型的一个副本
在inheritPrototype()函数中所做的事:
1.在inheritPrototype函数中用到了原型式继承中的object()方法,将超类型的原型指定为一个临时的空构造函数的原型,并返回构造函数的实例。
2.此时由于构造函数内部为空(不像SuperType里面有实例属性),所以返回的实例也不会自带实例属性,这很重要!因为后面用它作为SubType的原型时,就不会产生无用的原型属性了,借调构造函数也就不用进行所谓的“重写”了。
3.然后为这个对象重新指定constructor为SubType,并将其赋值给SubType的原型。这样,就达到了将超类型构造函数的实例作为子类型原型的目的,同时没有一些从SuperType继承过来的无用原型属性。
本面试题为前端常考面试题,后续有机会继续完善。我是歌谣,一个沉迷于故事的讲述者。
欢迎一起私信交流。
“睡服“面试官系列之各系列目录汇总(建议学习收藏)
“约见”面试官系列之常见面试题第三十八篇之js常见的继承方式(建议收藏)相关推荐
- “约见”面试官系列之常见面试题第三十九篇之异步更新队列-$nextTick(建议收藏)
目录 一,前言 二,什么是异步更新队列 三,使用异步更新队列 四,结尾 一,前言 这一篇介绍有关异步更新队列的知识,通过异步更新队列的学习和研究能够更好的理解Vue的更新机制 二,什么是异步更新队列 ...
- “约见”面试官系列之常见面试题之第九十五篇之vue-router的组件组成(建议收藏)
<router-link :to='' class='active-class'> //路由声明式跳转 ,active-class是标签被点击时的样式<router-view> ...
- “约见”面试官系列之常见面试题之第九十二篇之created和mounted区别(建议收藏)
beforeCreate 创建之前:已经完成了 初始化事件和生命周期 created 创建完成:已经完成了 初始化注册和响应 beforeMount 挂载之前:已经完成了模板渲染 mounted :挂 ...
- “约见”面试官系列之常见面试题第四十二篇之原型和原型链(建议收藏)
原型和原型链的理解:(面试题) 原型:每个函数都有 prototype 属性,该属性指向原型对象:使用原型对象的好处是所有对象实例共享它所包含的属性和方法. 原型链:主要解决了继承的问题:每个对象都拥 ...
- “约见”面试官系列之常见面试题第三十六篇之CSS常见兼容性问题及解决方案(建议收藏)
CSS常见兼容性问题及解决方案: 1. 上下margin重合问题: 问题描述:相邻的margin-left和margin-right是不会重合的,但是相邻的块级元素margin-top 和margin ...
- “约见”面试官系列之常见面试题之第九十九篇之router的钩子函数(建议收藏)
当使用路由参数时,例如从 /user/aside导航到 /user/foo,原来的组件实例会被复用.因为两个路由都渲染同个组件,比起销毁再创建,复用则更加高效.不过,这也意味着组件的生命周期钩子不会再 ...
- “约见”面试官系列之常见面试题之第六十七篇之jsonp原理和实现(建议收藏)
一. 同源策略 所有支持Javascript的浏览器都会使用同源策略这个安全策略.看看百度的解释: 同源策略,它是由Netscape提出的一个著名的安全策略. 现在所有支持JavaScript 的浏览 ...
- “约见”面试官系列之常见面试题之第六十三篇之get和post区别(建议收藏)
GET和POST是HTTP请求的两种基本方法,要说它们的区别,接触过WEB开发的人都能说出一二. 最直观的区别就是GET把参数包含在URL中,POST通过request body传递参数. 你可能自己 ...
- “约见”面试官系列之常见面试题之第五十三篇之网站的资源优化(建议收藏)
本面试题为前端常考面试题,后续有机会继续完善.我是歌谣,一个沉迷于故事的讲述者. 欢迎一起私信交流. "睡服"面试官系列之各系列目录汇总(建议学习收藏)
最新文章
- kotlin设置CORS跨域资源共享,java设置允许跨域,服务端如何设置 springboot中设置跨域资源共享
- 最新组合式模型量化方法,实现FPGA最高硬件利用率,准确率-推理速度达到SOTA...
- Visual Studio 2008 每日提示(二十三)
- 如何循序渐进向DotNet架构师发展
- leetcode 220. Contains Duplicate III | 220. 存在重复元素 III (Treeset解法+分桶解法)
- 决策树的选择,哪个放在第一个需要决策的环节
- Map.Entry如何使用?
- GPU+Cuda8.0+cudnn8+OpenCv2.4.13+Caffee 安装教程嘎嘎
- java爬虫12306_java爬虫12306,爬取所有的站点和车次,并导入postgreSQL数据库
- QT视频采集之编码Enc和录像Rec
- KMS知识文档管理系统如何与BPM流程管理相结合
- 2020找工作更难了?做好这4方面,找到高薪好工作
- 大数据开发:基于Hadoop的数据分析平台
- html word 编辑表格,在Word文档中运用编辑表格的7个技巧
- mercury路由器重置后服务器无响应,MERCURY无线路由器重置后重新设置不能 – 手机爱问...
- 002稀疏数组和队列[超大章]
- 几行代码!用 Python 画漂亮、专业的插图
- 【opencv】高频低频滤波
- Jabber 技 术 概 况
- Socket基础八:网络IO模型的应用