重学JavaScript之面向对象的程序设计(继承)
1. 继承
ES 中只支持实现继承,而且其实现继承主要依靠原型链来实现的。
2. 原型链
ES中 描述了 原型链的概念,并将原型链作为实现继承的主要方法。其基本思想是利用原型让一个引用类型继承另一个引用类型的属性和方法。
回顾一下构造函数、原型和实例的关系
每个构造函数都有一个原型对象,原型对象都包含一个指向构造函数的指针,而实例都包含一个指向原型对象的内部指针。那么假如我们让原型对象等于另一个类型的实例。那么此时的原型对象将包含一个指向另一个原型的指针,相应地,另一个原型中也包含着一个指向另一个构造函数的指针。
另外,假如另一个原型又是另一个类型的实例,如此层层递进,就构成了实例与原型的链条。这就是原型链的基本概念。
function SuperType(){
this.property = true
}
SuperType.prototype.getSuperValue = function(){
retrun this.property
}
function SubType(){
this.subproperty = false
}
// 继承了 SuperType
SubType.prototype = new SuperType()
SubType.prototype.getSubValue = function(){
retrun this.subproperty;
}
var instance = new SubType()
instance.getSuperValue() // true
上面代码中定义了两个类型:
SuperType 和SubType。每个类型分别有一个属性和一个方法。它们的主要区别是:
SubType继承了SuperType,而继承是通过创建SuperType的实例,并将该实例赋给SubType.prototype实现的。实现的本质是重写原型对象,代之以一个新类型的实例。
也就是说,原来存在于SuperType的实例中的所有方法和属性,现在也存在于SubType.prototype中。在给SubType.prototype添加一个方法后,这样就在继承了SuperType的属性和方法的基础上又添加了一个新的方法。这样就实现了实例以及构造函数和原型之间的关系。
通过实现原型链,本质上扩展了原型搜索机制,当以读取模式访问一个实例属性时,首先会在实例中搜索该属性。如果没有找到该属性,则会继续搜索实例的原型。在通过原型链实现继承的情况下,搜索过程就得以沿着原型链继续向上。直到最后一步找到该方法。在找不到属性和方法的情况下,搜索过程总是要一环一环地前行到原型链末端才会停下来。
3. 默认的原型
所有引用类型默认都继承了Object,而这个继承也是通过原型链实现的。所有函数的默认原型都是Object的实例 因此默认原型都会包含一个内部指针,指向 Object.prototype。这也是所有自定义类型都会继承 toString()、valueOf()默认方法的原因
4. 原型和实例的关系
可以通过两种方式来确定原型和实例之间的关系。第一种方式就是使用 instanceof操作符,只要用这个操作符来测试实例与原型链中出现过的构造函数,结果就会返回 true。如下:
instance instanceof Object // true
第二种方法就是 isPrototypeOf() 方法,同样,只要原型链中出现过的原型,都可以说是该原型链所派生的实例原型,因此 isPrototypeOf() 方法也会返回 true
Object.prototype.isPrototypeOf(instance) // true
5. 谨慎地定义方法
子类型有时候需要重写超类型中的某个方法,或者需要添加超类型中不存在的某个方法,但是无论如何,给原型添加方法的代码一定要放在替换原型的语句之后
function SuperType() {
this.property = true
}
SuperType.prototype.getSuperValue = function(){
retrun this.property
}
function SubType() {
this.subproperty = false
}
----
// 继承了SuperType
SubType.prototype = new SuperType()
// 添加新方法
SubType.prototype.getSubValue = function(){
retrun this.subproperty
}
//重写超类型中的方法
SubType.prototype.getSuperValue = function(){
retrun false
}
-----
var instance = new SubType()
instance.getSuperValue() // false
以上代码中分隔的部分是两个方法的定义。
第一个方法 getSubValue()被添加到了SubType中,第二个方法 getSuperValue()是原型链中已经存在的一个方法,但重写这个方法将会屏蔽原来那个方法.
换句话说,当 SubType 的实例调用 getSuperValue()时,调用的就是这个重新定义的方法,但通过 SuperType的实例调用 getSuperValue()时,还会继续调用原来的那个方法,所有必须要在用SuperType的实例替换原型之后,在定义这两个方法。
注意:即在通过原型链实现继承的时候,不能使用对象字面量创建原型方法,因为这样做会重写原型。
6. 原型链的问题
原型链虽然很强大,可以用它来实现继承,但也存在一定的问题。
1、来自包含引用类型值的原型。在之前说过包含引用类型值的原型属性会被所有实例共享。所以这也是为什么要在构造函数中,而不是在原型对象中定义属性的原因。在通过原型来实现继承时,原型实际上会变成另一个类型的实例。于是,原先的实例属性也就顺理成章地变成了现在的原型属性。
2、在创建子类型的实例时,不能向超类型的构造函数中传递参数。实际上可以说是没有办法再不影响所有对象实例的情况下,给超类型的构造函数传递参数a。
7. 借用构造函数
利用在子类型构造函数的内部调用超类型构造函数。即可以通过 apply() 和 call()方法在新创建的对象上执行构造函数。
7.1 传递参数
相对于原型链而言,借用构造函数有一个很大的优势,即可以在子类型构造函数中向超类型构造函数传递参数。
function s(name) {
this.name = name
}
function b() {
// 继承 s,同时还传递参数
s.call(this, 'nnn')
// 实例属性
this.age = 23
}
let i = new B()
i.name // nn
i.age // 29
7.2 借用构造函数的问题
如果仅仅是借用构造函数,那么也就无法避免构造函数模式存在的问题,方法都在构造函数中定义,因此函数复用就无法实现。另外,在超类型的原型中定义的方法,对子类型而言也是不可见的。结果所有类型都只能使用构造 函数模式。所以借用构造函数模式很少单独使用。
8. 组合继承
也叫做伪 经典继承,指的是将原型链和借用构造函数的技术组合到一块。发挥二者的长处的一种继承模式。原理就是使用原型链实现对原型属性和方法的继承,而通过借用构造函数来实现对实例属性的继承。
9. 原型式继承
借助已有的对象创建新对象,先创建一个临时的构造函数,然后将传入的对象作为这个构造函数的原型,最后返回这个临时类型的新实例。
10. 寄生式继承
创建一个仅用于封装继承过程的函数,该函数在内部以某种方式增强对象,然后再返回对象。同样也是不能做到函数复用而会降低效率
11. 总结
ES 支持面向对象编程,但不使用类或接口。对象可以在代码执行过程中创建和增强,因此具有动态性而非严格定义的实体。在没有类的情况下,可以采用 工厂模式、构造函数模式、原型模式创建对象。
11.1 工厂模式
使用简单的函数创建对象,为对象添加属性和方法,然后返回对象。这个模式被构造函数模式所取代
11.2 构造函数模式
创建自定义引用类型,可以像创建内置对象实例一样使用 new 操作符。不过,构造函数模式也有缺点,即它的每个成员无法得到复用,包括函数。由于函数可以不局限于任何对象。因此没有理由不再多个对象之间共享函数。
11.3 原型模式
使用构造函数的 prototype 属性来指定那些应该共享的属性和方法。组合使用构造函数模式和原型模式时,使用构造函数定义实例属性,而使用原型定义共享的属性和方法。
JS主要通过原型链实现继承。原型链的构建是将一个类型的实例赋值给另一个构造函数的原型实现。这样,子类型就能够访问超类型的所有属性和方法。这点和基于类的继承很相似。
原型链的问题就是对象实例共享所有继承的属性和方法,因此不适合单独使用。 如果想解决这个问题就需要借助于构造函数,即在子类型构造函数的内部调用超类型构造函数。这样就可以做到每个实例都具有自己的属性,同时还能保证只使用构造函数模式来定义类型。
11.4 原型式继承
可以在不预先定义构造函数的情况下实现继承,其本质是执行对给定对象的浅复制。而复制得到的副本还可以进一步改造
11.5 寄生式继承
与原型式继承非常相似,也是基于某个对象或某些信息创建一个对象,然后增强对象,最后返回对象。为了解决组合继承模式由于多次调用超类型构造函数而导致低效率问题,可以将这个模式和组合继承一起使用
11.6 寄生组合式继承
集寄生式继承和组合继承的优点于一身,是实现基于类型继承的最有效方式
关注公众号 【小夭同学】
重学JavaScript之面向对象的程序设计(继承)的更多相关文章
- (C#、JavaScript)面向对象的程序设计
面向对象(OOP)的理解 喜欢程序的朋友们,大家应该都听过一句话"万物皆对象",感觉老牛X了. 面向对象的程序设计,它是围绕真实世界来设计程序的. 面向对象三要素:封装.继承.多态 ...
- javascript 高级程序设计学习笔记(面向对象的程序设计)继承
ECMAScript中描述了原型链的概念,原型链是实现继承的主要方法. 实现原型链继承有一种基本模式 function SuperType () { this.property = true; } S ...
- C#&java重学笔记(面向对象)
C#部分 1.C#有一个internal关键字,指字段可以同一个程序集中访问,出了程序集不行.还有一个protected internal(没有先后之分)修饰词,指只能在同一个程序集中的子类访问 2. ...
- JavaScript高级 面向对象的程序设计 (一)《JavaScript高级程序设计(第三版)》
创建对象 继承 面向对象的语言都有一个表示---类.通过类我们可以创建多个具有相同属性的对象.但是,在JS中并没有类的概念,所以JS的对象也和其他语言的对象不同. 对象的定义:无序的属性集合,其属性可 ...
- JavaScript高级 面向对象的程序设计 (二)《JavaScript高级程序设计(第三版)》
二.继承 OO是面向对象语言最为有魅力的概念.一般的OO语言都实现了两种继承,接口继承和实现继承.接口继承只继承方法签名,而实际继承继承了实际的方法. 而在JS中,函数没有签名,所以无法实现接口继承. ...
- 重学JavaScript - 数组
作者:狐狸家的鱼 GitHub:surRimn 整理自MDN文档 数组 数组是一种类列表对象,长度和元素类型不固定. 描述 访问数组 JavaScript数组的索引是从0开始的,第一个元素的索引为0, ...
- 【JavaScript】面向对象的程序设计
一.前言 接着上一篇的内容,继续JavaScript的学习. 二.内容 属性类型 //数据属性[Configurable] —— 能否通过delete删除属性从而重新定义属性,能否修改属 ...
- 重学JavaScript - 映射与集合
作者:狐狸家的鱼 GitHub:surRimn 整理自MDN文档 带键的集合 映射 Map对象 一个Map对象在迭代时会根据对象中元素的插入顺序来进行 — 一个 for...of 循环在每次迭代后会返 ...
- JavaScript之面向对象学习八(继承)
简介:继承是OO语言中的一个最为人津津乐道的概念.许多OO语言都支持两种继承方式:接口继承和实现继承.接口继承只继承方法签名,而实现继承则继承实际的方法. 但是JS的函数并没有签名,所以在ECMASc ...
随机推荐
- C#数据结构_图
顶点的度=顶点的入度+顶点的出度. 顶点 v 的入度是指以该顶点 v 为弧头的弧的数目:顶点 v 的出度是指以该顶点 v 为弧尾的弧的数目. 简单路径:一条路径上顶点不重复出现. 回路:第一个顶点和最 ...
- JS-对象的深浅拷贝及其新增方法测试
我们在了解数据类型的时候,都知道一般的字符,数值,undefined和布尔值等都为一般数据类型,它们在进行数据传输的时候,进行的是值传递,当我们修改新数据,是不影响老数据的.但是我们今天要讲的是数据类 ...
- Delphi - cxGrid颜色显示相关设置
1:单元格的值满足某个条件时,该单元格所在整行颜色设置整行字体设置 选中cxGridDBTableView,单击F11调出属性配置面板,在Events中双击OnCustomDrawCell后双击编辑重 ...
- 01 Python 基础数据类型
基础数据类型,有7种类型,存在即合理. 1.int 整数 主要是做运算的 .比如加减乘除,幂,取余 + - * / ** %...2.bool 布尔值 判断真假以及作为条件变量3.str 字符串 存 ...
- HDU- 3605 - Escape 最大流 + 二进制压位
HDU - 3605 : acm.hdu.edu.cn/showproblem.php?pid=3605 题目: 有1e5的的人,小于10个的星球,每个星球都有容量,每个人也有适合的星球和不适合的星球 ...
- CodeForces-768B-Code For 1+DFS类似线段树思想
Code For 1 题意:对于一个n,可以将它分解为n/2,n%2,n/2三个数字,重复上述操作知道虽有值为1或0为止: 求L---R区间数列的和: 思路:首先画着画着可以发现这是一个类似线段数的结 ...
- Minimum spanning tree for each edge(倍增LCA)
https://vjudge.net/contest/320992#problem/J 暑期训练的题. 题意:给你一个n个点,m条边的无向图.对于每一条边,求包括该边的最小生成树. 思路:首先想到求一 ...
- CodeForces 85D Sum of Medians Splay | 线段树
Sum of Medians 题解: 对于这个题目,先想到是建立5棵Splay,然后每次更新把后面一段区间的树切下来,然后再转圈圈把切下来的树和别的树合并. 但是感觉写起来太麻烦就放弃了. 建立5棵线 ...
- CodeForces round 520 div2
A:A Prank 题意:给定一个递增序列, 问最多能删除多少个连续数字,要求删除数字之后能还原成原来的数列. 题解:直接找就好了,为了方便可以使得第0个数字为0, 第n+1个元素为1001 代码: ...
- CF1003D Coins and Queries 贪心
Coins and Queries time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...