我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧!

上一次我们分享了用JavaScript创建对象封装一个类,这次我们来分享如何用JavaScript实现继承和多态。

类式继承

我们首先声明一个父类

function ParentClass(){
this.perentValue=true;
}

然后我们为父类添加公有方法

ParentClass.prototype.getPerentValue=function(){
return this.perentValue;
};

接着我们声明一个子类

function ChildClass(){
this.childValue=false;
}

然后我们继承之前我们声明的父类

ChildClass.prototype=new ParentClass();

接着我们为子类添加公有方法

ChildClass.prototype.getChlidValue=function(){
return this.childValue;
}

看上去这和我们之前分享的封装很像,只不过我们将第一个类的实例赋值给了第二个类的原型,但是我们为什么要这么做呢?

类的原型对象就是为类的原型添加公有属性和添加公有方法,但是类不能直接访问这些属性和方法,必须通过原型prototype来访问。当我们实例化一个父类的时候,新创建的对象复制了父类构造函数里的属性和方法,并且将原型_protp_指向了父类的原型对象。

__proto__是一个对象拥有的内置属性(请注意:prototype是函数的内置属性,__proto__是对象的内置属性),是JS内部使用寻找原型链的属性。
用chrome和FF都可以访问到对象的__proto__属性,IE不可以。

那么我们总结一下,类式继承的原理:

我们将父类对象赋值给子类的原型,那么这个子类的原型就可以访问父类原型上的属性和方法同时也可以访问从父类构造函数中赋值的属性和方法
我们可以通过点语法直接去访问父类的属性和方法
var inheritance=new ChildClass();
console.log(inheritance.getPerentValue());//true
console.log(inheritance.getChlidValue());//false

我们可以通过instanceof来检查某个对象是否是某个类的实例,来进一步确认继承关系

console.log(inheritance instanceof ParentClass);//true
console.log(inheritance instanceof ChildClass);//true

我们可以看到对于inheritance对象,ChildClass和ParentClass都是他的实例,为什么会这样呢?

因为instanceof是通过对象的prototype链来确定这个对象是否是某个类的实例而不关心对象与类的自身结构。

那么ChildClass继承于ParentClass那么ParentClass会是ChildClass的实例吗,我们再来打印看一下。

console.log(ChildClass instanceof ParentClass);//false

答案是false,为什么会出现这种情况呢?ChildClass确实继承于ParentClass呀。

因为我们在实现ChildClass继承于ParentClass时,是将ParentClass的实例赋值给ChildClass的原型prototype,所以是ChildClass的原型prototype继承了ParentClass。

console.log(ChildClass.prototype instanceof ParentClass);//true

类式继承的缺点

  1. 由于子类通过其原型prototype对父类实例化继承了父类,如果父类中的公有属性是引用类型,那么子类中所有的实例都能共用,那么就是说如果一个子类的实例更改了子类原型从其父类构造函数中继承的公有属性,就会直接影响所有其他继承于这个父类的子类。
function ParentClass(){
this.parentValue=['C#','JAVA','PHP'];
}
function ChildClass(){};
ChildClass.prototype=new ParentClass();
var inheritance1=new ChildClass();
var inheritance2=new ChildClass();
console.log(inheritance1.parentValue);//['C#','JAVA','PHP']

我们会看到此时,会打印出父类里的属性值,那么如果我们现在往父类的属性值中追加一个值又会怎样呢?

inheritance1.parentValue.push('C++');
console.log(inheritance2.parentValue);//['C#','JAVA','PHP,C++'']

我们看到inheritance2的属性也发生了变化,这在我们的编程中往往很容易埋下隐患。

  1. 由于子类实现的继承是其原型prototype对父类实例化实现的,所以在创建父类的时候,是无法向父类传递参数的,因而在实例化父类的时候也无法对父类的构造函数类的属性进行初始化

那我们如果去解决这些问题呢?我们接着往下看

构造函数继承

同样的我们先声明一个父类

function ParentClass(id){
//引用类型的公有属性
this.parentValue=['C#','JAVA','PHP'];
//值类型公有属性
this.id=id;
}

接着我们声明父类的原型方法

ParentClass.prototype.showParents=function(){
console.log(this.parentValue);
}

下面我们声明子类

function ChildClass(id){
//继承父类
ParentClass.call(this,id);
}

创建第一个子类的实例

var inheritance1=new ChildClass(1);

创建第二个子类的实例

var inheritance2=new ChildClass(2);

接着我们跟之前类式之前一样,往第一个实例的父类里追加一个值,看看有什么变化。

inheritance1.parentValue.push('C++');
console.log(inheritance1.parentValue);//['C#','JAVA','PHP']
console.log(inheritance1.id);//1
console.log(inheritance2.parentValue);//['C#','JAVA','PHP,C++'']
console.log(inheritance2.id);//2

我们看到之前的问题解决了,这是为什么呢?

因为在继承父类的时候我们使用了call这个方法,这个方法可以更改函数的作用环境,所以在子类中对ParentClass调用实际上就是将子类中的变量在父类中执行一遍,由于父类中是通过this绑定属性的所以子类也就继承了父类的公有属性,我们之前讲个通过this继承的对象每次创建的时候都会创建一个新的实例,所以父类的原型方法不会被子类继承。我们可以打印出来看一看。

inheritance1.showParents();//Uncaught TypeError: inheritance1.showParents is not a function

我们可以看到他抛了一个类型错误的异常:inheritance1.showParents不是一个方法。

构造函数继承的缺点

  1. 这种类型的继承不涉及到原型prototype,所以父类的原型方法也不会被子类继承,如果想要被子类继承就必须要放在构造函数中,但是这样创建出来的每个实例都会单独拥有一份而不能被公用,这样就违背了代码复用的原则。

那么我们如何去解决这个问题呢?

组合继承

从名字我们不难看出组合继承,是一种组合结构,他结合了类式继承,和构造函数继承的优点,他通过在子类的构造器中执行父类的构造函数,在子类原型上实例化父类就是组合继承,这样就融合了类式继承和构造函数继承的优点,并且过滤掉了他们两的缺点。

那么具体怎么实现呢?我们来看下面的例子。

首先我们还是先声明一个父类

function ParentClass(id){
//引用类型的公有属性
this.parentValue=['C#','JAVA','PHP'];
//值类型公有属性
this.id=id;
}

同样的我们给父类声明原型方法

ParentClass.prototype.getParentValue=function(){
console.log(this.id);
}

接着我们声明子类

function ChildClass(id,childValue){
//通过构造函数继承,继承父类的id属性
ParentClass.call(this,id);
//子类新增公用属性
this.childValue=childValue;
}

紧接着我们使用类式继承

ChildClass.prototype=new ParentClass();

同样的添加子类的原型方法

ChildClass.prototype.getChildValue=function(){
console.log(this.childValue);
}

这样我们的组合继承就写好了,我们看到组合继承即使用构造函数继承又使用了类式继承,接下来我们调用看一看。

创建第一个子类的实例

var inheritance1=new ChildClass(1,'hello');

我们往parentValue追加一个值

inheritance1.parentValue.push('C++');
console.log(inheritance1.parentValue);//['C#','JAVA','PHP,C++'']

接着我们再看看调用方法会不会抛之前TypeError的异常

inheritance1.getParentValue();//1
inheritance1.getChildValue();//hello

创建第二个子类的实例

var inheritance2=new ChildClass(2,'word');

最后我们看看组合继承还会出现类式继承或构造器继承出现的问题吗?

console.log(inheritance2.parentValue);//['C#','JAVA','PHP']
inheritance2.getParentValue();//2
inheritance2.getChildValue();//word

现在我们看到,子类的实例化中更改了父类继承下来的引用属性parentValue,但是根本不会影响到其他的实例,这就解决了类式继承的弊端,同时子类实例化过程中又能将参数id传递到父类的构造函数中,这就解决了构造继承的弊端。

那组合继承是不是最完美的继承呢,其实并不是,我们来看在组合继承中,我们用构造器执行了一遍父类的构造函数,在实现子类原型的类式继承又调用了一遍父类的构造函数,因此父类构造函数被调用两次所以还不是最完美的继承方式。

那么最完美的方式是什么呢?,javascript中多态又如何体现呢?我们将在下一章中来一一解答。

也谢谢大家看到这里:)如果你觉得我的分享还可以请点击推荐,分享给你的朋友让我们一起进步~

好了以上就是本次分享的全部内容,本次示例参考自JavaScript设计模式一书,让我们一点点积累一点点成长,希望对大家有所帮助。

再起航,我的学习笔记之JavaScript设计模式03的更多相关文章

  1. 再起航,我的学习笔记之JavaScript设计模式02

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...

  2. 再起航,我的学习笔记之JavaScript设计模式01

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 在通 ...

  3. 再起航,我的学习笔记之JavaScript设计模式04

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 上回 ...

  4. 再起航,我的学习笔记之JavaScript设计模式05(简单工程模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  5. 再起航,我的学习笔记之JavaScript设计模式06(工厂方法模式)

    上一次已经给大家介绍了简单工厂模式,相信大家对创建型设计模式有了初步的了解,本次我将给大家介绍的是工厂方法模式. 工厂方法模式 工厂方法模式(Factory Method):通过对产品类的抽象使其创建 ...

  6. 再起航,我的学习笔记之JavaScript设计模式05(简单工厂模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  7. 再起航,我的学习笔记之JavaScript设计模式06(抽象工厂模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前两 ...

  8. 再起航,我的学习笔记之JavaScript设计模式08(建造者模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 前几 ...

  9. 再起航,我的学习笔记之JavaScript设计模式09(原型模式)

    我的学习笔记是根据我的学习情况来定期更新的,预计2-3天更新一章,主要是给大家分享一下,我所学到的知识,如果有什么错误请在评论中指点出来,我一定虚心接受,那么废话不多说开始我们今天的学习分享吧! 我们 ...

随机推荐

  1. nmon用法

    一.简介 nmon是一个简单的性能监测工具,可以监测CPU.内存.网络等的使用情况.它是一个系统监视.调优.性能测试工具,它能一次性提供大量性能相关的信息. 二.安装与执行 下载地址:http://n ...

  2. 并发编程(三):全视角解析volatile

    一.目录 1.引入话题-发散思考 2.volatile深度解析 3.解决volatile原子性问题 4.volatile应用场景 二.引入话题-发散思考 public class T1 { /*vol ...

  3. form表单上传文件使用multipart请求处理

    在开发Web应用程序时比较常见的功能之一,就是允许用户利用multipart请求将本地文件上传到服务器,而这正是Grails的坚固基石——spring MVC其中的一个优势.Spring通过对Serv ...

  4. 【Android Developers Training】 87. 序言:同步到云

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  5. axis1.4开发webservice客户端(快速入门)-基于jdk1.4

    写在前面: 对于客户端,服务端开发好了以后,客户端只需要调用就可以了.这里我们讲的是,根据服务的访问地址,来生成客户端所需要用到的代码(听说有几种调用方式,但是用到最常见的就是stub方式,貌似我说的 ...

  6. 前端解读Webview

    作为盛行已久的开发方式,Hybrid的相关介绍已经是相当普遍了.不过看到博客园里基本上都是从android或者ios的角度来讲解的,对于h5的前端来说看起来只能是一直半解.感觉有必要从前端的角度来理解 ...

  7. web前段学习2017.6.15

    CSS---表现层,修饰和表现html文档,为了解决结构层和表现层分离的问题. 通过CSS极大的提高了工作效率,方便工作人员维护和管理CSS:层叠样式表,目前用的最广泛的css版本为css2,最新版本 ...

  8. ZooKeeper介绍,安装,配置文件解析

    什么是ZooKeeper? ZooKeeper是用于维护配置信息,命名,提供分布式同步和提供组服务的集中式服务. 所有这些类型的服务都以分布式应用程序以某种形式或另一种形式使用.每次实施时,都有很多工 ...

  9. js模块化/js模块加载器/js模块打包器

    之前对这几个概念一直记得很模糊,也无法用自己的语言表达出来,今天看了大神的文章,尝试根据自己的理解总结一下,算是一篇读后感. 大神的文章:http://www.css88.com/archives/7 ...

  10. How to install MySQL on CentOS

    1)chekc centos中是否安装了MySQL [root@localhost MySQL]# rpm -qa | grep mariadb mariadb-libs-5.5.52-1.el7.x ...