引言:继承是面向对象语言中的一个最为人津津乐道的概念。许多面向对象都支持两种继承方式:接口继承与实现继承,接口继承只继承方法签名,而实现继承则继承实际的方法。如前所述,由于JavaScript 中函数没有签名,所以在ECMAScript 中无法实现接口继承。ECMAScript 只支持实现继承,而实现继承主要依靠原型链来实现的。

原型链:

概念:每个构造函数都要一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的指针,那么假如我们让原型对象等于另一个类型的实例结果会怎么样呢?显然,此时的原型对象将包含一个指向另一个原型的指针,相应的,另一个原型中也包含着一个指向另一个构造函数的指针。假如另一个原型又是另一个类型的实例,那么上述关系依然成立,如此层层递进,就构成了实例与原型的链条。

说的太抽象了,我们直接看案例:实现原型链有一种基本模式:

function SuperType(){this.property = true;}SuperType.prototype.getSuperValue = function(){return this.property;};function SubType(){this.subproperty = false;}//继承了SuperType

SubType.prototype = new SuperType();SubType.prototype.getSubValue = function (){return this.subproperty;};var instance = new SubType();alert(instance.getSuperValue()); //true

如图:

我们没有使用 SubType 默认提供的原型,而是给它换了一个新原型;这个新原型 就是 SuperType 的实例。于是,新原型不仅具有作为一个 SuperType 的实例所拥有的全部属性和方法, 而且其内部还有一个指针,指向了 SuperType 的原型。最终结果就是这样的:instance 指向 SubType 的原型, SubType 的原型又指向 SuperType 的原型。 getSuperValue() 方法仍然还在 SuperType.prototype 中,但 property 则位于 SubType.prototype 中。这是因为 property 是一 个实例属性,而 getSuperValue()则是一个原型方法。既然 SubType.prototype 现在是 SuperType的实例,那么 property 当然就位于该实例中了。此外,要注意 instance.constructor 现在指向的 是 SuperType,这是因为原来 SubType.prototype 中的 constructor 被重写了的缘故(① 实际上,不是 SubType 的原型的 constructor 属性被重写了,而是 SubType 的原型指向了另一个对象—— SuperType 的原型( SubType.prototype = new SuperType()),而这个原型对象的 constructor 属性指向的是 SuperType。)。

别忘记默认的原型:

我们知道,所有引用类型默认都继承了 Object,而 这个继承也是通过原型链实现的。大家要记住,所有函数的默认原型都是 Object 的实例,因此默认原型都会包含一个内部指针,指向 Object.prototype。这也正是所有自定义类型都会继承 toString()、 valueOf()等默认方法的根本原因。所以,我们说上面例子展示的原型链中还应该包括另外一个继承层 次。

如图:

一句话,SubType 继承了 SuperType,而 SuperType 继承了 Object。当调用 instance.toString() 时,实际上调用的是保存在 Object.prototype 中的那个方法。

确定原型与实例的关系:

确定原型和实例的关系 可以通过两种方式来确定原型和实例之间的关系。第一种方式是使用 instanceof 操作符。

第二种:是使用 isPrototypeOf()方法。同样,只要是原型链中出现过的原型,都可以说是该 原型链所派生的实例的原型,因此 isPrototypeOf()方法也会返回 true

举个例子:

alert(Object.prototype.isPrototypeOf(instance)); //true

谨慎地定义方法:

子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法。但不管怎 样,给原型添加方法的代码一定要放在替换原型的语句之后。来看下面的例子

  

function SuperType(){this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType(); //添加新方法
SubType.prototype.getSubValue = function (){return this.subproperty;
};//重写超类型中的方法SubType.prototype.getSuperValue = function (){return false;};var instance = new SubType();alert(instance.getSuperValue()); //false

加粗的部分是两个方法的定义。第一个方法 getSubValue()被添加到了 SubType 中。第二个方法 getSuperValue()是原型链中已经存在的一个方法,但重写这个方法将会屏蔽原来的 那个方法。换句话说,当通过 SubType 的实例调用 getSuperValue()时,调用的就是这个重新定义 的方法;但通过 SuperType 的实例调用 getSuperValue()时,还会继续调用原来的那个方法。这里 要格外注意的是,必须在用 SuperType 的实例替换原型之后,再定义这两个方法(也就是上面的://继承了 SuperType SubType.prototype = new SuperType(); )

PS :值得注意的是:通过原型链实现继承时2,不能用对象字面量创建原型方法,因为这样会重写原型链。比如我们将上面的代码改成

function SuperType(){this.property = true;}SuperType.prototype.getSuperValue = function(){ return this.property;};function SubType(){this.subproperty = false;
}
//继承了 SuperType
SubType.prototype = new SuperType();
//使用字面量添加新方法,会导致上一行代码无效
SubType.prototype = { getSubValue : function (){return this.subproperty;
},
someOtherMethod : function (){
return false;
}
};var instance = new SubType();
alert(instance.getSuperValue()); //error!

以上代码展示了刚刚把 SuperType 的实例赋值给原型,紧接着又将原型替换成一个对象字面量而 导致的问题。由于现在的原型包含的是一个 Object 的实例,而非 SuperType 的实例,因此我们设想 中的原型链已经被切断——SubType 和 SuperType 之间已经没有关系了。

原型链的问题

上一章节我们提到,原型模式的自身问题,那就是原型可以实现属性共享,而这也正是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性了。举个例子:

 1 function SuperType(){ this.colors = ["red", "blue", "green"];
 2
 3 }
 4
 5 function SubType(){ }
 6
 7 //继承了
 8
 9 SuperType SubType.prototype = new SuperType();
10
11 var instance1 = new SubType();
12
13 instance1.colors.push("black");
14
15 alert(instance1.colors); //"red,blue,green,black"
16
17 var instance2 = new SubType(); alert(instance2.colors); //"red,blue,green,black"

  这个例子中的 SuperType 构造函数定义了一个 colors 属性,该属性包含一个数组(引用类型值)。 SuperType 的每个实例都会有各自包含自己数组的 colors 属性。当 SubType 通过原型链继承了 SuperType 之后,SubType.prototype 就变成了 SuperType 的一个实例,因此它也拥有了一个它自 己的 colors 属性——就跟专门创建了一个 SubType.prototype.colors 属性一样。但结果是什么 呢?结果是 SubType 的所有实例都会共享这一个 colors 属性。而我们对 instance1.colors 的修改 能够通过 instance2.colors 反映出来,就已经充分证实了这一点。

   原型链的第二个问题是:在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上, 应该说是没有办法在不影响所有对象实例的情况下,给超类型的构造函数传递参数。有鉴于此,再加上 前面刚刚讨论过的由于原型中包含引用类型值所带来的问题,实践中很少会单独使用原型链。

----------------------------------------------------------------------本章节完------------------------------------------------------------

下一章节预告:本章节我们总结学习了继承,同时也了解到原型链继承机制碰到的问题,那么这个问题我们会在下一章节进行学习总结。

(JavaScript的继承是这个语言的重点部分,我在写这篇博文时一直在想怎么总结才好,要不要自己用自己的理解与语言重写,纠结了好一会后自己写了一篇,但是不如人意,决定还是使用书中的文字,以后随着深入,再重开一篇单独讲继承。还有一个比较尴尬,我现在才发现博客的“插入代码”功能可以帮助更好的排版,所以我又得重新将前面的博文重新排版了)

转载于:https://www.cnblogs.com/wxhhts/p/9479926.html

《JavaScript 高级程序设计》学习总结六(3)相关推荐

  1. JavaScript高级程序设计学习笔记(三)

    分享一下第五章(引用类型)的笔记,内容比较多,我拆成了两部分,今天这部分是关于Object.Array.Date和RegExp类型的. 以下的笔记是书上一些我以前学习的时候,没有太重视的js基础知识, ...

  2. javascript高级程序设计学习笔记

    javascript高级程序设计,当枕头书已经好久了~zz  现在觉得自己在js的开发上遇到了一些瓶颈,归根究底还是基础太薄弱,所以重新刷一遍js高程希望有更新的认识. 一.javascript简介 ...

  3. javascript高级程序设计学习之数值转换 |Number(),parseInt(),parseFloat()

    2019独角兽企业重金招聘Python工程师标准>>> 将非数值转换成数值的函数有三个:Number(),parseInt(),parseFloat(); 小记tip:Number( ...

  4. [Javascript 高级程序设计]学习心得记录 函数参数传递与引用

    最近开始啃js的红宝书:<Javascript 高级程序设计>,偶有心得,记录一下. 先上代码 function howManyArgs() {alert(arguments.length ...

  5. JavaScript高级程序设计--学习笔记(第六章)

    文章目录 第六章 面向对象的程序设计 1. 理解对象 1.1 属性类型 1.2 定义多个属性 1.3 读取属性的特性 2. 创建对象 2.1 工厂模式 2.2 构造函数模式 2.3 原型模式 2.4 ...

  6. JavaScript高级程序设计学习(六)之设计模式

    每种编程语言都有其自己的设计模式.不禁让人疑惑设计模式是用来做什么?有什么用? 简单的说,设计模式是为了让代码更简洁,更优雅,更完美. 同时设计模式也会让软件的性能更好,同时也会让程序员们更轻松.设计 ...

  7. JavaScript高级程序设计学习(四)之引用类型(续)

    一.Date类型 其实引用类型和相关的操作方法,远远不止昨天的所说的那些,还有一部分今天继续补充. 在java中日期Date,它所属的包有sql包,也有util包.我个人比较喜欢用util包的.理由, ...

  8. javascript高级程序设计 学习笔记 第五章 上

      第五章   引用类型的值(对象)是引用类型的一个实例.在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起.它也常被称为类,但这种称呼并不妥当.尽管 ECMAScri ...

  9. 《javascript高级程序设计》第六章 读书笔记 之 javascript对象的几种创建方式

    本文首发于https://segmentfault.com/a/1190000017776314 一.工厂模式 工厂模式:使用字面量和object构造函数会有很多重复代码,在此基础上改进 解决了多个相 ...

  10. JavaScript高级程序设计学习笔记二(在HTML中使用JavaScript)

    在 HTML 中使用 JavaScript 在html中使用JavaScript脚本有两种方式一种是嵌入在HTML中的脚本,另一种是引入外部的脚本.两种方式都离不开<script>元素. ...

最新文章

  1. 微软中国 CTO:请把 AI 拉下神坛
  2. Vue.js 第二天: 列表渲染
  3. MyBatis相应API
  4. 理解向日葵甘特之六——定义数据列
  5. ASP.NET2.0 XML系列(6): 使用XmlReaderSettings配置XmlReader的输出
  6. 【Python】Jupyter Notebook 配置路径
  7. micropython esp32驱动舵机_PCA9685舵机控制板与MicroPython-ESP32-1Z实验室
  8. springboot 获取bean_3W 字的 Spring Boot 超详细总结
  9. mie散射理论方程_Mie氏散射理论的实验研究
  10. (5)FPGA面试题同步电路和异步电路
  11. python logging详解及自动添加上下文信息
  12. 北京互联网人寒冬求职记
  13. wmf和emf格式的图片有什么区别? JPG图片如何转换WMF文件?
  14. module ‘cv2‘ has no attribute ‘SIFT‘
  15. hacker 入门指南
  16. 初始C语言(初学者福音)
  17. Ps简单几步把人物图片转素描画
  18. 数据库—应用系统开发方法
  19. 谁都有追逐梦想的权利
  20. Linux常用命令——telnet命令

热门文章

  1. Spring-引用Bean的属性值
  2. Oracle存储过程和自定义函数
  3. WebView完全解读
  4. 微积分笔记(一)--预备知识
  5. 【两分钟带你了解树】数据结构04-树结构的概述
  6. Dubbo暴露服务过程
  7. r k-means 分类结果_R语言信用评分卡:数据分箱(binning)
  8. c#样条曲线命令_如何定制CAD功能区界面中的命令?
  9. vue项目实现记住密码到cookie功能
  10. substring、substr以及slice、splice用法和区别