JavaScript中的原型继承原理

    在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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

建模元件有哪些在MapleSim中

信号库:包含通用信号模块、布尔、控制器、离散信号模块、信号源、线性信号模块、非线性信号模块、时间离散信号模块、查询表、信号转换器、数学运算、关系元件、特殊信号模块,应用案例。 电子库:包含电阻、运算放大器、二极管、步进电机、模拟…

【C++】VS2010将写好的程序打包成安装文件发布

参考链接:http://blog.csdn.net/yongh701/article/details/51326142 我们可以将自己写好的VS2010程序打包成安装文件,给用户安装,具体步骤如下: 1、如下图,同样是新建一个项目,但是这次是新建一个其它项目…

01_jeecms建站

一、环境安装 JDK5TOMCAT5.5MYSQL5及以上http://www.jeecms.com/tutorial/index.jhtml参考环境安装篇二、解压文件安装包jeecms-v5zip,如图图1ROOT文件夹复制放到tomcat下的webapps文件夹(注:请先删除webapps下原有的默认ROOT文件夹)如不想部…

WiFi基本知识

转自:http://blog.csdn.net/myarrow/article/details/7930131 1. IE802.11简介 标准号IEEE 802.11bIEEE 802.11aIEEE 802.11gIEEE 802.11n标准发布时间1999年9月1999年9月2003年6月2009年9月工作频率范围2.4-2.4835GHz 5.150-5.350GHz5.475&a…

libev 宏展开

想看源码,宏太多,看着累,宏展开,再看,功力时间不够,先放下 放上宏展开后的代码。 libev4.20 展开方示为 ./configure 修改makefile文件,字符串 替换CC为 CPP 注意要把基础的CC定义保留 make mv …

FreeRTOS高级篇7---FreeRTOS内存管理分析

原文:http://blog.csdn.net/zhzht19861011/article/details/51606068 内存管理对应用程序和操作系统来说都非常重要。现在很多的程序漏洞和运行崩溃都和内存分配使用错误有关。 FreeRTOS操作系统将内核与内存管理分开实现,操作系统内核仅规定了必要的内…

FreeRTOS学习笔记——互斥型信号量

来自:http://blog.csdn.net/xukai871105/article/details/43456985 0.前言 在嵌入式操作系统中互斥型信号量是任务间资源保护的重要手段。下面结合一个具体例子说明FreeRTOS中的互斥型信号量如何使用。 【相关博文】 【FreeRTOS STM32移植笔记】 【FreeRTOS学习笔记…

FreeRTOS系列第19篇---FreeRTOS信号量

来自:http://blog.csdn.net/zhzht19861011/article/details/50835613 本文介绍信号量的基础知识,详细源码分析见《FreeRTOS高级篇6---FreeRTOS信号量分析》 1.信号量简介 FreeRTOS的信号量包括二进制信号量、计数信号量、互斥信号量(以后简称…

蓝牙HCI剖析(一)

来自:http://blog.csdn.net/xiaoxiaopengbo/article/details/51334257 一.HCI介绍 HCI提供了访问bluetooth control的统一接口,通俗来讲,就是定义了特定的格式来控制蓝牙芯片来做相应的动作(比如inquiry,connect,disconnect&#…

ASP.NET状缓存Cache的应用-提高数据库读取速度

ASP.NET状缓存Cache的应用-提高数据库读取速度 原文:ASP.NET状缓存Cache的应用-提高数据库读取速度一、 Cache概述 既然缓存中的数据其实是来自数据库的,那么缓存中的数据如何和数据库进行同步呢?一般来说,缓存中应该存放改动不大或者对…

入门级----测试的执行、环境的搭建、每日构建、测试记录和跟踪、回归测试、测试总结和报告...

测试用例的准备,都是为了执行测试准备的。 测试环境的搭建 (1)测试数据:有些测试需要使用大批量的数据,例如容量测试、压力测试等。根据产品的具体测试要求,可能需要在数据库表插入大量的数据,准…

限制MySQL Binlog的传输速率

最近一台核心库备库完成恢复后打开slave,导致主库传送binlog,瞬间占满网络,触发故障。 为了做一些限制, 给mysql在发送binlog的函数(mysql_binlog_send)里每隔一段时间sleep一次, 增加了两个参数: master_s…

掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求

转http://www.ibm.com/developerworks/cn/xml/wa-ajaxintro2/ 掌握 Ajax,第 2 部分: 使用 JavaScript 和 Ajax 发出异步请求 在 Web 请求中使用 XMLHttpRequest 多数 Web 应用程序都使用请求/响应模型从服务器上获得完整的 HTML 页面。常常是点击一个按钮&#xff0…

Provisioning Services 7.8 入门系列教程之十一 通过版本控制自动更新虚拟磁盘

续Provisioning Services 7.8 入门系列教程之十 通过类自动更新虚拟磁盘从前两的两种更新方式可以看出,它们有一个共同的特点,即需要产生(复制)完成的虚拟磁盘副本,然后进行相关的升级操作。这两种方法在实际生产中&am…

登录失败时记住访问的地址

登录失败时记住访问的地址 使用spring MVC 访问时,在拦截器中记录访问的地址: Java代码 String path request.getRequestURI();//"/demo_channel_terminal/news/list" System.out.println("您无权访问:" path); //用于登录成功…

Tomcat - Maven plugin: 运行找不到webapp

2019独角兽企业重金招聘Python工程师标准>>> The tomcat7-maven-plugin allows running the current project as a Web application and additional <webapps> can be specified that will be simultaneously loaded into tomcat. My project is not a Web ap…

基于MQTT协议进行应用开发

来自&#xff1a;http://www.cnblogs.com/secondtononewe/p/6073089.html 官方协议有句如下的话来形容MQTT的设计思想&#xff1a; “It is designed for connections with remote locations where a "small code footprint" is required or the network bandwidth i…

SortedDictionaryTKey,TValue正序与反序排序及Dicttionary相关

SortedDictionary<TKey,TValue>能对字典排序 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace SortDictionary {class Program{static void Main(string[] args){TestDictionarySort();…

DOS窗口的编码页从UTF-8调回GBK

2019独角兽企业重金招聘Python工程师标准>>> 之前在DOS窗口操作MySQL数据库的时候&#xff0c;将编码页从GBK设置成了UTF-8&#xff0c;解决了在DOS窗口显示MySQL数据库中的表中的中文字符出现乱码的问题。但是除此之外&#xff0c;DOS窗口显示的其他中文字符都是乱…

UIBezierPath

学习UIBezierPath画图 笔者在写本篇文章之前&#xff0c;也没有系统学习过贝塞尔曲线&#xff0c;只是曾经某一次的需求需要使用到&#xff0c;才临时百度看了一看而且使用最基本的功能。现在总算有时间停下来好好研究研究这个神奇而伟大的贝塞尔先生&#xff01; 笔者在学习时…