全面分析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-TreeView父子联动效果保持节点状态一致
Aug 12 Javascript
JavaScript CSS修改学习第五章 给“上传”添加样式
Feb 19 Javascript
JQuery中操作Css样式的方法
Feb 12 Javascript
jQuery验证插件 Validate详解
Nov 20 Javascript
用js读、写、删除Cookie代码续篇
Dec 03 Javascript
兼容Firefox的Javascript XSLT 处理XML文件
Dec 31 Javascript
jQuery使用hide方法隐藏页面上指定元素的方法
Mar 30 Javascript
常用原生JS兼容性写法汇总
Apr 27 Javascript
jQuery四种选择器使用及示例
Jun 05 Javascript
Jquery EasyUI Datagrid右键菜单实现方法
Dec 30 Javascript
原生jQuery实现只显示年份下拉框
Dec 24 jQuery
如何在CocosCreator里画个炫酷的雷达图
Apr 16 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
List all the Databases on a SQL Server
2007/06/21 Javascript
javascript按位非运算符的使用方法
2013/11/14 Javascript
ExtJS4给Combobox设置列表中的默认值示例
2014/05/02 Javascript
easyui Droppable组件实现放置特效
2015/08/19 Javascript
jQuery实现可高亮显示的二级CSS菜单效果
2015/09/01 Javascript
jQuery插件WebUploader实现文件上传
2016/11/07 Javascript
微信小程序 开发之滑块视图容器(swiper)详解及实例代码
2017/02/22 Javascript
鼠标经过出现气泡框的简单实例
2017/03/17 Javascript
解决AngualrJS页面刷新导致异常显示问题
2017/04/20 Javascript
使用vue框架 Ajax获取数据列表并用BootStrap显示出来
2017/04/24 Javascript
实例讲解javascript实现异步图片上传方法
2017/12/05 Javascript
Node.js使用MySQL连接池的方法实例
2018/02/11 Javascript
微信小程序下拉框组件使用方法详解
2018/12/28 Javascript
JavaScript跳出循环的三种方法(break, return, continue)
2019/07/30 Javascript
js实现掷骰子小游戏
2019/10/24 Javascript
[01:30]2016国际邀请赛中国区预选赛神秘商店火爆开启
2016/06/26 DOTA
Python的迭代器和生成器使用实例
2015/01/14 Python
在Python中使用M2Crypto模块实现AES加密的教程
2015/04/08 Python
基于Python os模块常用命令介绍
2017/11/03 Python
Python Web程序部署到Ubuntu服务器上的方法
2018/02/22 Python
解决python中使用PYQT时中文乱码问题
2019/06/17 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
使用索引有什么好处
2016/07/27 面试题
面向对象设计的原则是什么
2013/02/13 面试题
创业计划书的主要内容有哪些
2014/01/29 职场文书
致铅球运动员加油稿
2014/02/13 职场文书
工程造价专业大学生职业规划范文
2014/03/09 职场文书
《赠汪伦》教学反思
2014/04/12 职场文书
高中生操行评语
2014/04/25 职场文书
股份合作协议书
2014/09/10 职场文书
个人三严三实对照检查材料
2014/09/25 职场文书
临时用工协议书范本
2014/10/29 职场文书
企业法人任命书
2015/09/21 职场文书
甜美蛋糕店的创业计划书模板,拿来即用!
2019/08/21 职场文书
dubbo服务整合zipkin详解
2021/07/26 Java/Android
JavaScript利用html5新方法操作元素类名详解
2021/11/27 Javascript