全面分析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 相关文章推荐
jquery中ajax调用json数据的使用说明
Mar 17 Javascript
js中的时间转换—毫秒转换成日期时间的示例代码
Jan 26 Javascript
JavaScript数组函数unshift、shift、pop、push使用实例
Aug 27 Javascript
JQuery fileupload插件实现文件上传功能
Mar 18 Javascript
Jquery操作cookie记住用户名
Mar 29 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
Aug 25 Javascript
分分钟玩转Vue.js组件(二)
Mar 01 Javascript
jquery dataTable 获取某行数据
May 05 jQuery
基于vue、react实现倒计时效果
Aug 26 Javascript
微信小程序实现轨迹回放的示例代码
Dec 13 Javascript
基于Angular 8和Bootstrap 4实现动态主题切换的示例代码
Feb 11 Javascript
小程序自定义轮播图圆点组件
Jun 25 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
PHP array操作10个小技巧分享
2011/06/23 PHP
浅析使用Turck-mmcache编译来加速、优化PHP代码
2013/06/20 PHP
php获取从html表单传递数组的方法
2015/03/20 PHP
PHP实现合并discuz用户
2015/08/05 PHP
标准版Eclipse搭建PHP环境的详细步骤
2015/11/18 PHP
在WordPress的文章编辑器中设置默认内容的方法
2015/12/29 PHP
php实现简单四则运算器
2020/11/29 PHP
使用原生js封装webapp滑动效果(惯性滑动、滑动回弹)
2014/05/06 Javascript
js全选按钮的实现方法
2015/11/17 Javascript
Jquery树插件zTree实现菜单树
2017/01/24 Javascript
vue轮播图插件vue-awesome-swiper的使用代码实例
2017/07/10 Javascript
javascript变量提升和闭包理解
2018/03/12 Javascript
微信小程序实现发送验证码按钮效果
2018/12/20 Javascript
vue实现的上拉加载更多数据/分页功能示例
2019/05/25 Javascript
移动端 Vue+Vant 的Uploader 实现上传、压缩、旋转图片功能
2019/06/10 Javascript
vue+vant使用图片预览功能ImagePreview的问题解决
2020/04/10 Javascript
详解ES6实现类的私有变量的几种写法
2021/02/10 Javascript
使用Python装饰器在Django框架下去除冗余代码的教程
2015/04/16 Python
Python上传package到Pypi(代码简单)
2016/02/06 Python
Python数据结构之哈夫曼树定义与使用方法示例
2018/04/22 Python
解决python os.mkdir创建目录失败的问题
2018/10/16 Python
对python制作自己的数据集实例讲解
2018/12/12 Python
python实现海螺图片的方法示例
2019/05/12 Python
django之静态文件 django 2.0 在网页中显示图片的例子
2019/07/28 Python
python实现各种插值法(数值分析)
2019/07/30 Python
Python 炫技操作之合并字典的七种方法
2020/04/10 Python
python 代码实现k-means聚类分析的思路(不使用现成聚类库)
2020/06/01 Python
Python 列表中的修改、添加和删除元素的实现
2020/06/11 Python
解决margin 外边距合并问题
2019/07/03 HTML / CSS
如何配置、使用和清除Smarty缓存
2015/12/23 面试题
SOA的常见陷阱或者误解是什么
2014/10/05 面试题
旅游管理毕业生自荐信
2013/11/05 职场文书
宝宝满月酒主持词和仪式流程
2014/03/27 职场文书
晚会开幕词
2015/01/28 职场文书
干部培训工作总结2015
2015/05/25 职场文书
Html5生成验证码的示例代码
2021/05/10 Javascript