在JavaScript当中,对象A如果要继承对象B的属性和方法,那么只要将对象B放到对象A的原型链上即可。而某个对象的原型链,就是由该对象开始,通过__proto__属性连接起来的一串对象。__proto__属性是JavaScript对象中的内部属性,任何JavaScript对象,包括我们自己构建的对象,JavaScript的built-in对象,任何函数(在JavaScript当中,函数也是对象)都具有这个属性。如下图就是一个原型链的例子:

上图中,A,B,C分别代表3个对象,蓝色箭头串接起来的所有对象就构成了对象C的原型链,其中C的_proto__属性指向B,B的__proto__属性指向A,A的__proto__属性可能指向更高层的对象,也可能指向null(表示A不继承任何对象的属性和方法)。如果我们引用了C的某个属性或者方法,那么JavaScript就会顺着C的原型链进行查找,即首先查找对象C本身,看所引用的属性名或者方法名是否存在,如果存在就停止查找直接返回,如果不存在,就通过C的__proto__属性找到原型链中的B对象,继续在B对象中查找,如果B对象中找到所引用的属性名或者方法名,那么就停止查找直接返回,如果B对象中也不存在,就通过对象B的__proto__属性找到原型链中的A对象,继续重复上述查找过程,直到找到所引用的属性或者方法为止(同时也可能查找完对象C的整个原型链也没有找到所引用的属性或者方法,那么该属性或者方法就是undefined的)。

因此,只要能够成功的为某一个对象构造出我们需要的原型链,那么就能让该对象继承我们想要它继承的方法或者属性。而想要成功构造对象的原型链,就还必须理解prototype属性,JavaScript当中已经存在的原型链,以及当我们创建对象时,原型链被构造的过程。

prototype属性

  prototype属性存在于JavaScript的任何函数当中,这个属性指向的对象就是所谓的原型对象,在构造原型链时需要原型对象。

JavaScript当中已经存在的原型链

  在JavaScript当中存在Object,Function,Array,String,Boolean,Number,Date,Error,RegExp这9个built-in函数一个built-in的Math对象,通过这上述9个built-in函数我们可以创建相应的对象,同时,这9个built-in函数的prototype属性所指向的原型对象也是built-in的。下面的图示解释了这几个函数以及各自prototype属性所指向的原型对象之间的关系。

(如果此图看不清,可点击此处下载)

  上面的图示中,黄色方框代表built-in函数对象,深绿色方框代表built-in函数prototype属性指向的原型对象,名字都叫xx prototype object,浅绿色方框(即Math对象)代表普通对象,蓝色箭头连接非built-in函数对象(无论是普通对象如Math,还是原型对象)的__proto__属性,而土黄色箭头连接函数对象的__proto__属性。

  通过上图可以发现,所有built-in函数对象的原型链最终都指向Function prototype object,所有非函数对象的原型链最终都指向Object prototype object,并且Function prototype object的__proto__属性也指向Object prototype object,Object prototype object的__proto__属性指向为null。因此,Object prototype object是所有原型链的顶端。因此,通过原型链查找规则可知,所有built-in函数对象同时继承了Object prototype object和Function prototype object上的属性和方法,而所有非built-in函数对象只继承了Object prototype object上的方法。Function prototype object包含了所有函数共享的属性和方法,而Object prototype object包含了所有对象都共享额属性和方法。

  对于上图中原型对象包含的constructor属性,下文当中有解释。

创建对象时,原型链的构造过程

在JavaScript当中创建对象有2中方式,一种是通过定义函数使用new方法来构造,另一种是使用对象字面量的方式,即:

var obj = {name: "Jim Green"
};

使用这两种方式创建对象时,对象的原型链构造过程有所不同。

1 使用函数的方式构造对象

  使用函数的方式构造对象分为两步:首先需要定义一个函数作为构造函数,然后使用new方法构造对象。接下来就来看一下这两个步骤会发生什么。

  假设我们定义了一个函数名为F才,此时JavaScript会为我们做两件事,第一:根据我们定义的函数创建一个函数对象,第二,设置这个函数的__proto__属性和prototype属性。其中__proto__属性指向built-in的Function prototype object,而prototype属性指向一个为函数对象F新创建的原型对象,这个新创建的原型对象通过调用new Object()构造出来,并且为这个新创建的对象添加constructor属性,该属性指向函数对象F。最后的结果如下图所示:

  上图中为了方便,没有画出Function prototype object和Object prototype object的constructor属性。而F prototype object的__proto__属性为何指向Object prototype object,下文介绍new操作符时有解释。

当我们使用new方法调用F函数的时候,JavaScript也会为我们做两件事,第一,分配内存作为新创建的对象,第二,将新创建的对象的__proto__属性指向函数F的原型对象,结果如下图:

上图中,obj就是调用new方法通过函数F创建出来的对象,我们可以看到对象obj的原型链包含了函数F的原型对象,以及Object prototype object,这样,对象obj通过原型链查找规则,就能继承函数F的原型对象,以及Object prototype object上面定义的属性和方法了。并且如果我们想知道一个对象是由哪个方法构建的,只需要访问这个对象的constructor属性即可,上例中,只要我们访问obj.constructor,那么就知道obj是由函数F创建的。同时,由于F prototype object上文中介绍是由new Object函数创建的,根据此处介绍,F prototype object的__proto__属性应该指向Object函数的原型对象,即Object prototype object。

2 使用对象字面量定义对象

当使用对象字面量创建对象时,JavaScript会为我们做两件事:

1 分配内存作为新创建的对象

2 将新创建对象的__proto__属性指向Object prototype object

结果如下图所示:

上图为了简化,同样没有画出Object prototype object的constructor属性

继承

  理解了上面所讲的原理之后,假设目前有一个对象A(这个对象可以是任意的,包括JavaScript built-in的对象,任何函数对象,任何原型对象,以及我们自己new出来的对象),现在想创建一个对象obj,让obj继承A的属性和方法。通过上面的介绍,我们知道创建对象有两种方式,但是使用对象字面量创建的对象其原型链总是只包含两个对象,一个是其自己,一个是Object prototype object,根本不可能包含对象A,无法达到让对象obj继承对象A属性和方法的效果。因此,只能使用函数的方式创建对象,让对象A包含在新创建对象obj的原型链中即可。根据上面的讲解,如果是用函数的方式创建对象,那么在调用new方法时,新创建对象的__proto__属性会指向函数的原型对象。因此,只要在调用函数之前,将函数的原型对象换成A,然后再调用new方法,就可以将对象A包含在新创建的对象obj的原型链中,这样通过原型链查找规则,obj就继承了A的属性和方法。假设用来创建对象obj的函数为B,则相关代码为:

B.prototype = A;
B.prototype.constructor = B;
var obj = new B(传入的参数)

上面代码中的B.prototype.constructor = B,是因为对象A中可能没有constructor属性,或者constructor属性不指向B,而为了方便通过访问任何由B函数创建的对象的constructor属性,就可以正确的知道该对象是使用函数B构造出来的。相关图示如下图:

上图中虚线框所包围的B prototype object就是定义函数B时,JavaScript为函数B生成的原型对象,但是该对象被我们用代码替换成了对象A。由于这个被替换的B prototype object没有其他地方再用到,因此会被回收掉。

参考资料:

EcmaScript Language Specification 5

转载于:https://www.cnblogs.com/chaoguo1234/p/5023929.html

JavaScript中的原型继承原理相关推荐

  1. 理解JavaScript中的原型继承(2)

    两年前在我学习JavaScript的时候我就写过两篇关于原型继承的博客: 理解JavaScript中原型继承 JavaScript中的原型继承 这两篇博客讲的都是原型的使用,其中一篇还有我学习时的错误 ...

  2. JavaScript中一个对象如何继承另外一个对象

    如题,JavaScript中一个对象a如何继承另外一个对象b.即将b中的属性和方法复制到a中去. 面试中遇到了这个问题,当时脑子里的想法是: 1.除了循环遍历复制,还能怎样 2.javascript中 ...

  3. JavaScript中的原型和继承

    请在此暂时忘记之前学到的面向对象的一切知识.这里只需要考虑赛车的情况.是的,就是赛车. 最近我正在观看 24 Hours of Le Mans ,这是法国流行的一项赛事.最快的车被称为 Le Mans ...

  4. JavaScript中的原型(prototype)与继承

    在JavaScript中,原型是用来模仿其他「类」语言继承机制的基础.原型并不复杂,原型只是一个对象. 一.原型对象 1.1 什么是原型对象 每当我们创建了一个函数后,这个函数就拥有了一个protot ...

  5. 理解JavaScript中的原型与原型链

    理解JavaScript中的原型与原型链 原型链是一种机制,指的是JavaScript中每个内置的对象都有一个内置的__proto__属性指向创建它的构造函数的prototype(原型)属性.原型链的 ...

  6. 理解原型设计模式以及JavaScript中的原型规则

    原型规则 原型规则 所有的引用类型(数组.对象.函数),都具有对象特征,即可自由扩展属性: var arr = []; arr.a =1; 所有的引用类型都有对象的特性,即可自由扩展 所有的引用类型都 ...

  7. JavaScript中函数式编程的原理

    要了解JavaScript中的函数式编程原理,必须理解一下两个知识点: 1,JavaScript中函数.方法的调用 在JavaScript中,有两种调用函数的方式.一般的方式是把参数放在括号中,另一种 ...

  8. 【JavaScript】js中的原型继承

    文章目录 1. 理解继承 2. js中的继承(原型继承) 3. js6种继承方式 3.1 原型链继承 3.2 盗用构造函数 3.3 组合继承 3.4 原型式继承 3.5 寄生式继承 3.6 寄生式组合 ...

  9. js实现html模板继承,理解JavaScript中的原型和继承

    本文主要讲了原型如何在JavaScript中工作,以及如何通过[Prototype]所有对象共享的隐藏属性链接对象属性和方法:以及如何创建自定义构造函数以及原型继承如何工作以传递属性和方法值. 介绍 ...

最新文章

  1. pytorch lstm crf 代码理解 重点
  2. Maven 的classifier的作用
  3. linux权限切换命令,Linux基础常用命令汇总(权限操作)
  4. python 字符串分割_python中分割字符串split切割并选择输出 逐行读取文件后字符串拼接...
  5. C 常用新特性(上)
  6. 做ppt用的小插图_如何用PPT做随机抽奖?
  7. linux 访问共享内存,Linux下的共享内存(03)---通过指针访问共享内存中的数据...
  8. arcgis xml 下载 切片_arcgis api 4.x for js地图加载arcgisserver本地离线瓦片(附源码下载)...
  9. 卸载loadrunner
  10. htc+one+m7+linux驱动,HTC One M7简易刷Recovery教程
  11. Hive内部表和外部表
  12. python3.4.3如何获取文件的路径
  13. IGT-DSER智能网关不用PLC编程,实现多台不同品牌PLC之间的通讯案例
  14. 双参数cfar c语言代码,一种多目标环境下的SAR图像双参数CFAR检测方法与流程
  15. GEO数据挖掘(学习笔记)
  16. 2022数学建模高教杯国家一等奖经验分享
  17. 《数据库系统概论》第四版课后习题答案
  18. 流氓软件 电脑软件管家
  19. 【软考系统架构设计师】计算机组成原理与体系结构章节习题集
  20. 第3章 分布式操作系统的进程和处理机

热门文章

  1. 【POJ - 1664】放苹果 (递归经典题 或 dp 或 母函数)
  2. 【CodeForces - 334B】Eight Point Sets(水题模拟,有坑)
  3. 【HDU - 4217 】Data Structure? (线段树求第k小数)
  4. cancase vector_基于Vector总线设备的CAN总线测试方法概述
  5. android 判断空,Android空判断的坑
  6. java jframe添加面板_JFrame添加组件的两种方式
  7. Linux学习:第三章-Linux常用命令-1
  8. JBPM4.4整合SSH2项目
  9. leetcode237 删除链表中的节点(你意想不到的做法,注意细节)
  10. 炸窝(Java)拼接