全面分析JavaScript 继承


Posted in Javascript onMay 30, 2019

ES6之前,JavaScript并没有继承这一现有的机制。

ES5的继承方式

类式继承

//声明父类
function Father(){
this.fatherVal = 'father';
}
//为父类添加共有方法
Father.prototype.getFatherValue = function(){
return this.fatherVal;
}
//声明子类 
function Child(){
this.childVal = 'child';
}
//继承父类
Child.prototype = new Father();
//为子类添加共有方法
Child.prototype.getChildValue = function(){
return this.childVal;
}

子类的prototype被赋予父类的实例,新创建的对象复制了父类的构造函数内的属性和方法并且将原型_proto_指向了父类的原型对象,这样就拥有了父类的原型对象上的属性和方法与父类构造函数中复制的属性和方法。

var instance = new Child();
console.log(instance.getFatherValue()); //father
console.log(instance.getChildValue()); //child
console.log(instance instanceof Child); //true
console.log(instance instanceof Father); //true
console.log(instance instanceof Object); //true
console.log(Child instanceof Father); //false
console.log(Child.prototype instanceof Father); //true

缺点:

1.子类实例共用父类的公有引用属性。

2.无法对父类构造函数内的属性进行传参初始化。

function Father(){
this.companies =['bigo','yy','uc']
}
funtion Child(){}
Child.prototype = new Father();
var instanceA = new Child();
var instanceB = new Child();
console.log(instanceB.companies); //['bigo','yy','uc']
instanceA.companies.push('nemo');
console.log(instanceB.companies); //['bigo','yy','uc','nemo']

构造函数继承

//声明父类
function Father(val){
this.companies =['bigo','yy','uc']
this.val = val;
}
//声明父类原型方法
Father.prototype.getCom = function(){
console.log(this.companies);
}
//声明子类
function Child(val){
//继承
Father.call(this,val);
}
var instanceA = new Child('childA');
var instanceB = new Child('childB');
instanceA.companies.push('nemo');
console.log(instanceA.companies); //['bigo','yy','uc','nemo']
console.log(instanceA.val); //childA
console.log(instanceB.companies); //['bigo','yy','uc']
console.log(instanceB.val); //childB

对Child调用call,将子类中的变量在父类中执行一遍,然后父类给this绑定,所以子类继承了父类的公有属性。

缺点:

由于这种类型的继承没有设计原型prototype,所以父类的原型方法不会被子类继承,而如果想被子类继承就必须放在构造函数中,这样创建出来的每个实例都会单独拥有一份而不能共用。

组合继承

//声明父类
function Father(val){
this.companies =['bigo','yy','uc']
this.val = val;
}
//声明父类原型方法
Father.prototype.getValue = function(){
console.log(this.val);
}
//声明子类
function Child(val,newVal){
//构造函数式继承
Father.call(this,val);
this.newVal = newVal;
}
//类式继承
Child.prototype = new Father();
//声明子类原型方法
Child.prototype.getNewValue = function(){
console.log(this.newVal);
}
var instanceA = new Child("fatherA","childA");
instanceA.companies.push('nemo');
console.log(instanceA.companies); //['bigo','yy','uc','nemo']
instanceA.getValue(); //fatherA
instanceA.getNewValue(); //childA
var instanceB = new Child("fatherB","childB");
console.log(instanceA.companies); //['bigo','yy','uc']
instanceB.getValue(); //fatherB
instanceB.getNewValue(); //childB

缺点:

在使用构造函数继承使用执行了一遍父类的构造函数,在实现子类原型的类式继承再调用了一遍父类的构造函数,父类构造函数被调用了两次。

原型式继承

function inheritObject(obj){
function F(){};
F.prototype = obj;
return new F();
}
var situation = {
companies:['bigo','yy','uc'];
area:'guangzhou';
}
var situationA = inheritObject(situation);
situationA.area = 'shenzhen';
situationA.companies.push('tencent');
var situationB = inheritObject(situation);
situationB.area = 'beijing';
situationB.companies.push('baidu');
console.log(situationA.area); //shenzhen
console.log(situationA.companies); //['bigo','yy','uc','tencent','baidu']
console.log(situationB.area); //beijing
console.log(situationB.companies); //['bigo','yy','uc','tencent','baidu']
console.log(situation.area); //guangzhou
console.log(situation.companies); //['bigo','yy','uc','tencent','baidu']

是类式继承的一个封装,其中的过渡对象就相当于类式继承的子类,然后返回新的实例化对象。

缺点:

跟类式继承一样,父类的公有引用属性被共有。

寄生式继承

function inheritObject(obj){
function F(){};
F.prototype = obj;
return new F();
}
var situation = {
companies:['bigo','yy','uc'];
area:'guangzhou';
}
function createSituation(obj){
//通过原型继承创建新对象
var newObj = new inheritObject(obj);
//定义新对象方法
newObj.getArea = function(){
console.log(newObj.area)
}
//返回对象
return obj;
}

只是在原型式继承的基础上添加了新属性和方法,还是跟原型式继承一样的缺点。

寄生式组合继承

function inheritObject(obj){
function F(){};
F.prototype = obj;
return new F();
}
//传递参数 child,parent 子类父类
function inheritPrototype(child,parent){
//复制一份父类的原型副本保存在变量中;
var fatherProto = inheritObject(father.prototype);
//修正因为重写子类原型导致子类的constructor属性被修改;
fatherProto.constructor = child;
//设置子类的原型
child.prototype = fatherProto;
}
//声明父类
function Father(val){
this.companies =['bigo','yy','uc']
this.val = val;
}
//声明父类原型方法
Father.prototype.getValue = function(){
console.log(this.val);
}
//声明子类
function Child(val,newVal){
//构造函数式继承
Father.call(this,val);
this.newVal = newVal;
}
//类式继承
Child.prototype = new Father();
inheritPrototype(Child,Father);
//声明子类原型方法
Child.prototype.getNewValue = function(){
console.log(this.newVal);
}

1.在构造函数继承中我们已经调用了父类的构造函数,还差一个原型的副本

2.通过原型继承得到副本,但是这时候fatherProto的constructor需要指向子类。

3.最后将副本fatherProto赋给子类的原型prototype。

总的来说,就是既要构造函数,又要原型继承,但是又避免了组合继承的两次调用父类构造函数的问题,最大的改变式对子类原型赋予的式父类原型的一个引用。

var instanceA = new Child("fatherA","childA");
instanceA.companies.push('nemo');
console.log(instanceA.companies); //['bigo','yy','uc','nemo']
instanceA.getValue(); //fatherA
instanceA.getNewValue(); //childA
var instanceB = new Child("fatherB","childB");
console.log(instanceA.companies); //['bigo','yy','uc']
instanceB.getValue(); //fatherB
instanceB.getNewValue(); //childB

注意点:

此时子类如果需要添加原型方法,必须通过prototype点语法一个个添加,否则会覆盖掉继承父类的原型对象。
ES6 新增了Class语法,Class 可以通过extends关键字实现继承,这比 ES5 的通过修改原型链实现继承,要清晰和方便很多。

Class 继承

class Parent {
constructor(value) {
this.val = value
}
getValue() {
console.log(this.val)
}
}
class Child extends Parent {
constructor(value) {
super(value)
}
}
let child = new Child(1)
child.getValue() // 1
child instanceof Parent // true

class 实现继承的核心在于使用 extends 表明继承自哪个父类,并且在子类构造函数中必须调用 super,因为这段代码可以看成 Parent.call(this, value)。

如果子类没有定义constructor方法,这个方法会被默认添加。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript是否可实现多线程  深入理解JavaScript定时机制
Dec 22 Javascript
js中复制行和删除行的操作实例
Jun 25 Javascript
node.js中的socket.io入门实例
Apr 26 Javascript
window.open()详解及浏览器兼容性问题示例探讨
May 29 Javascript
JavaScript中三种异步上传文件方式
Mar 06 Javascript
微信小程序遇到修改数据后页面不渲染的问题解决
Mar 09 Javascript
JavaScript中的return布尔值的用法和原理解析
Aug 14 Javascript
vue-cli脚手架引入图片的几种方法总结
Mar 13 Javascript
前端路由&webpack基础配置详解
Jun 10 Javascript
五分钟搞懂Vuex实用知识(小结)
Aug 12 Javascript
微信小程序云开发获取文件夹下所有文件(推荐)
Nov 14 Javascript
node.js如何操作MySQL数据库
Oct 29 Javascript
浅谈Express.js解析Post数据类型的正确姿势
May 30 #Javascript
vue组件三大核心概念图文详解
May 30 #Javascript
详解一次Vue低版本安卓白屏问题的解决过程
May 30 #Javascript
基于iview的router常用控制方式
May 30 #Javascript
深入了解js原型模式
May 30 #Javascript
js逆向解密之网络爬虫
May 30 #Javascript
Vue.js中的组件系统
May 30 #Javascript
You might like
php5 图片验证码实现代码
2009/12/11 PHP
thinkphp中的url跳转用法分析
2016/07/12 PHP
php中钩子(hook)的原理与简单应用demo示例
2019/09/03 PHP
Yii使用EasyWechat实现小程序获取用户的openID的方法
2020/04/29 PHP
让getElementsByName适应IE和firefox的方法
2007/09/24 Javascript
验证码按回车不变解决方法
2013/03/29 Javascript
JavaScript高级教程5.6之基本包装类型(详细)
2015/11/23 Javascript
JS使用正则表达式过滤多个词语并替换为相同长度星号的方法
2016/08/03 Javascript
详解js的延迟对象、跨域、模板引擎、弹出层、AJAX【附实例下载】
2016/12/19 Javascript
基于vue2.0+vuex的日期选择组件功能实现
2017/03/13 Javascript
JS鼠标3次点击事件实现代码及扩展思路
2017/09/12 Javascript
react用Redux中央仓库实现一个todolist
2019/09/29 Javascript
使用JavaScript和MQTT开发物联网应用示例解析
2020/08/07 Javascript
js实现微信聊天效果
2020/08/09 Javascript
Ant Design Vue table中列超长显示...并加提示语的实例
2020/10/31 Javascript
以一段代码为实例快速入门Python2.7
2015/03/31 Python
在Python的Django框架中用流响应生成CSV文件的教程
2015/05/02 Python
Python使用dis模块把Python反编译为字节码的用法详解
2016/06/14 Python
Python实现线程状态监测简单示例
2018/03/28 Python
django传值给模板, 再用JS接收并进行操作的实例
2018/05/28 Python
Python爬虫实现获取动态gif格式搞笑图片的方法示例
2018/12/24 Python
Python实现实时数据采集新型冠状病毒数据实例
2020/02/04 Python
Python3 mmap内存映射文件示例解析
2020/03/23 Python
Python爬虫爬取微信朋友圈
2020/08/06 Python
将"引用"作为函数参数有哪些特点
2013/04/05 面试题
大三自我鉴定范文
2013/10/05 职场文书
业务部主管岗位职责
2014/01/29 职场文书
百年校庆节目主持词
2014/03/27 职场文书
教师求职信
2014/06/17 职场文书
大学竞选班干部演讲稿
2014/08/21 职场文书
公务员培的训心得体会
2014/09/01 职场文书
2014镇副书记群众路线专题民主生活会思想汇报
2014/09/23 职场文书
教师四风自我剖析材料
2014/09/30 职场文书
详细聊聊MySQL中慢SQL优化的方向
2021/08/30 MySQL
CSS使用伪类控制边框长度的方法
2022/01/18 HTML / CSS
使用Bandicam录制鼠标指针并附带点击声音,还可以添加点击动画效果
2022/04/11 数码科技