js原型继承的原理解析

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: js原型继承的原理解析

原文链接: mp.weixin.qq.com

在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当中存在ObjectFunctionArrayStringBooleanNumberDateErrorRegExp这9个built-in函数,一个 built-inMath对象,通过这上述9个 built-in函数我们可以创建相应的对象,同时,这9个built-in函数prototype属性所指向的原型对象也是built-in的。下面的图示解释了这几个函数以及各自 prototype属性所指向的原型对象之间的关系

(如果此图看不清,可打开 http://p3nqtyvgo.bkt.clouddn.com/20180927_02.png 下载 )

  上面的图示中,黄色方框代表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 objectObject prototype object__proto__属性指向为 null

因此,Object prototype object是所有原型链的顶端,通过原型链查找规则可知,所有 built-in函数对象同时继承了Object prototype objectFunction 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-inFunction prototype object,而prototype属性指向一个为函数对象F新创建的原型对象,这个新创建的原型对象通过调用 new Object()构造出来,并且为这个新创建的对象添加constructor属性,该属性指向函数对象 F。最后的结果如下图所示:

上图中为了方便,没有画出Function prototype objectObject prototype objectconstructor属性。而 Fprototype 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 objectconstructor属性


继承

理解了上面所讲的原理之后,假设目前有一个对象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没有其他地方再用到,因此会被回收掉。

目录
相关文章
|
2月前
|
存储 缓存 算法
HashMap深度解析:从原理到实战
HashMap,作为Java集合框架中的一个核心组件,以其高效的键值对存储和检索机制,在软件开发中扮演着举足轻重的角色。作为一名资深的AI工程师,深入理解HashMap的原理、历史、业务场景以及实战应用,对于提升数据处理和算法实现的效率至关重要。本文将通过手绘结构图、流程图,结合Java代码示例,全方位解析HashMap,帮助读者从理论到实践全面掌握这一关键技术。
113 14
|
3月前
|
运维 持续交付 云计算
深入解析云计算中的微服务架构:原理、优势与实践
深入解析云计算中的微服务架构:原理、优势与实践
134 3
|
6天前
|
机器学习/深度学习 算法 数据挖掘
解析静态代理IP改善游戏体验的原理
静态代理IP通过提高网络稳定性和降低延迟,优化游戏体验。具体表现在加快游戏网络速度、实时玩家数据分析、优化游戏设计、简化更新流程、维护网络稳定性、提高连接可靠性、支持地区特性及提升访问速度等方面,确保更流畅、高效的游戏体验。
53 22
解析静态代理IP改善游戏体验的原理
|
4天前
|
编解码 缓存 Prometheus
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
本期内容为「ximagine」频道《显示器测试流程》的规范及标准,我们主要使用Calman、DisplayCAL、i1Profiler等软件及CA410、Spyder X、i1Pro 2等设备,是我们目前制作内容数据的重要来源,我们深知所做的仍是比较表面的活儿,和工程师、科研人员相比有着不小的差距,测试并不复杂,但是相当繁琐,收集整理测试无不花费大量时间精力,内容不完善或者有错误的地方,希望大佬指出我们好改进!
47 16
「ximagine」业余爱好者的非专业显示器测试流程规范,同时也是本账号输出内容的数据来源!如何测试显示器?荒岛整理总结出多种测试方法和注意事项,以及粗浅的原理解析!
|
1月前
|
机器学习/深度学习 自然语言处理 搜索推荐
自注意力机制全解析:从原理到计算细节,一文尽览!
自注意力机制(Self-Attention)最早可追溯至20世纪70年代的神经网络研究,但直到2017年Google Brain团队提出Transformer架构后才广泛应用于深度学习。它通过计算序列内部元素间的相关性,捕捉复杂依赖关系,并支持并行化训练,显著提升了处理长文本和序列数据的能力。相比传统的RNN、LSTM和GRU,自注意力机制在自然语言处理(NLP)、计算机视觉、语音识别及推荐系统等领域展现出卓越性能。其核心步骤包括生成查询(Q)、键(K)和值(V)向量,计算缩放点积注意力得分,应用Softmax归一化,以及加权求和生成输出。自注意力机制提高了模型的表达能力,带来了更精准的服务。
|
2月前
|
存储 物联网 大数据
探索阿里云 Flink 物化表:原理、优势与应用场景全解析
阿里云Flink的物化表是流批一体化平台中的关键特性,支持低延迟实时更新、灵活查询性能、无缝流批处理和高容错性。它广泛应用于电商、物联网和金融等领域,助力企业高效处理实时数据,提升业务决策能力。实践案例表明,物化表显著提高了交易欺诈损失率的控制和信贷审批效率,推动企业在数字化转型中取得竞争优势。
120 16
|
2月前
|
网络协议 安全 网络安全
探索网络模型与协议:从OSI到HTTPs的原理解析
OSI七层网络模型和TCP/IP四层模型是理解和设计计算机网络的框架。OSI模型包括物理层、数据链路层、网络层、传输层、会话层、表示层和应用层,而TCP/IP模型则简化为链路层、网络层、传输层和 HTTPS协议基于HTTP并通过TLS/SSL加密数据,确保安全传输。其连接过程涉及TCP三次握手、SSL证书验证、对称密钥交换等步骤,以保障通信的安全性和完整性。数字信封技术使用非对称加密和数字证书确保数据的机密性和身份认证。 浏览器通过Https访问网站的过程包括输入网址、DNS解析、建立TCP连接、发送HTTPS请求、接收响应、验证证书和解析网页内容等步骤,确保用户与服务器之间的安全通信。
173 3
|
3月前
|
JavaScript 前端开发
JavaScript中的原型 保姆级文章一文搞懂
本文详细解析了JavaScript中的原型概念,从构造函数、原型对象、`__proto__`属性、`constructor`属性到原型链,层层递进地解释了JavaScript如何通过原型实现继承机制。适合初学者深入理解JS面向对象编程的核心原理。
49 1
JavaScript中的原型 保姆级文章一文搞懂
|
3月前
|
JavaScript 前端开发 API
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
Vue.js响应式原理深度解析:从Vue 2到Vue 3的演进
117 17
|
3月前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
124 1

热门文章

最新文章

推荐镜像

更多