全面分析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引用对象的方法
Jan 11 Javascript
原来Jquery.load的方法可以一直load下去
Mar 28 Javascript
浅谈Javascript鼠标和滚轮事件
Jun 27 Javascript
js加减乘除丢失精度问题解决方法
May 16 Javascript
Jquery实现$.fn.extend和$.extend函数
Apr 14 Javascript
JavaScript数据操作_浅谈原始值和引用值的操作本质
Aug 23 Javascript
js判断iframe中元素是否存在的实现代码
Dec 24 Javascript
详谈js使用in和hasOwnProperty获取对象属性的区别
Apr 25 Javascript
详解使用React进行组件库开发
Feb 06 Javascript
如何在vue里添加好看的lottie动画
Aug 02 Javascript
js中对象与对象创建方法的各种方法
Feb 27 Javascript
node.JS路径解析之PATH模块使用方法详解
Feb 06 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
无限级别菜单的实现
2006/10/09 PHP
php Ubb代码编辑器函数代码
2012/07/05 PHP
php注册登录系统简化版
2020/12/28 PHP
PHP微信企业号开发之回调模式开启与用法示例
2017/11/25 PHP
javascript 写的一个简单的timer
2009/07/30 Javascript
Mootools 1.2教程(2) DOM选择器
2009/09/14 Javascript
javascript Array对象基础知识小结
2010/11/16 Javascript
让你的CSS像Jquery一样做筛选的实现方法
2011/07/10 Javascript
js动态添加onclick事件可传参数与不传参数
2014/07/29 Javascript
javascript和jquery实现设置和移除文本框默认值效果代码
2015/01/13 Javascript
JavaScript实现自动对页面上敏感词进行屏蔽的方法
2015/07/27 Javascript
在其他地方你学不到的jQuery小贴士和技巧(欢迎收藏)
2016/01/20 Javascript
图文详解Heap Sort堆排序算法及JavaScript的代码实现
2016/05/04 Javascript
关于JavaScript 原型链的一点个人理解
2016/07/31 Javascript
Actionscript与javascript交互实例程序(修改)
2016/09/22 Javascript
Bootstrap select多选下拉框实现代码
2016/12/23 Javascript
nodejs multer实现文件上传与下载
2017/05/10 NodeJs
Vuejs 页面的区域化与组件封装的实现
2017/09/11 Javascript
nodejs socket服务端和客户端简单通信功能
2017/09/14 NodeJs
用Webpack构建Vue项目的实践
2017/11/07 Javascript
Python实现查找系统盘中需要找的字符
2015/07/14 Python
详解tensorflow载入数据的三种方式
2018/04/24 Python
Python生成器常见问题及解决方案
2020/03/21 Python
麦德龙官方海外旗舰店:德国麦德龙超市
2017/12/23 全球购物
护理毕业生自我鉴定
2014/02/11 职场文书
《小小雨点》教学反思
2014/02/18 职场文书
物理系毕业生自荐书
2014/06/13 职场文书
乡镇干部个人对照检查材料(群众路线)
2014/09/26 职场文书
工作检讨书怎么写
2014/10/10 职场文书
三严三实心得体会范文
2014/10/13 职场文书
2014年小学教学工作总结
2014/11/13 职场文书
刑事附带民事诉讼答辩状
2015/05/22 职场文书
那些美到让人窒息的诗句,值得你收藏!
2019/08/20 职场文书
Springboot如何使用logback实现多环境配置?
2021/06/16 Java/Android
英镑符号 £
2022/02/17 杂记
Echarts如何重新渲染实例详解
2022/05/30 Javascript