一文读懂原型链 prototype和__proto__详解
目录
1.原型对象 prototype
2.prototype 和 __proto__
3.原型链
4.补充
5.原型链总结
1.原型对象 prototype
我们首先总结一下原型对象的作用:
- 原型对象就相当于一个公共的区域,所有同一个类的实例都可以访问到这个原型对象,我们可以将对象中共有的内容,统一设置到原型对象中去。
- 当我们访问对象的一个属性或者方法时,它会先在对象自身中寻找,如果找到要查找的属性或者方法就会直接使用,如果没有就会去原型对象中查找,找到了就会直接使用。
- 在我们创建构造函数时,可以将对象的共有属性或方法,统一添加到构造函数的原型对象中去。这样做的好处就是不用分别为每一个对象添加这些共有的属性或方法,也不会影响到全局作用域,同时每个对象也都具有这些属性和方法。可以说给原型对象添加属性或者方法,函数的所有实例对象都会自动拥有原型中的属性和方法。
这里我们首先创建一个构造函数对象:
<script type="text/javascript">function MyDemo(){}//向MyDemo的原型中添加一个name属性 MyDemo.prototype.name = "JavaScript"var demo = new MyDemo()//向MyDemo实例对象中单独添加一个属性demo.id = "XUPT" //这里首先会去对象自身中去查找name属性,查找时发现没有,接着去原型对象中查找,找到则输出console.log(demo.name) // 输出 JavaScript//这里使用in检查对象中是否含有某个属性,如果对象中没有但是原型中有,同样也会返回true,反之返回falseconsole.log("id" in demo) //trueconsole.log("name" in demo) //true//这里使用对象的hasOwnProperty()来检查对象自身中是否含有该属性//hasOwnProperty()方法存在于原型对象中的__proto__中,后边会涉及讲解到__proto__console.log(demo.hasOwnProperty("id")) //true id属性存在于demo实例对象自身console.log(demo.hasOwnProperty("name")) //false name属性只存在于原型对象中
</script>
- 普遍来说,对于所有函数,每个函数是都会含有一个prototype属性,它默认指向一个object空对象(也称为原型对象)。
- 原型对象中都有一个constructor属性,它是指向函数对象的。
<script type="text/javascript">function fun(){}console.log(fun.prototype) //默认指向一个Object空实例对象,没有我们的属性//原型对象中都有一个constructor属性,它指向函数对象console.log(fun.prototype.constructor === fun) //true
</script>
2.prototype 和 __proto__
- 每个函数都有一个prototype属性,可以称之为显式原型属性。
- 每个对象的实例对象都有一个__proto__属性,可以称之为隐式原型属性。
- 实例对象的隐式原型的值和其构造函数的显式原型的值所对应。
<script type="text/javascript">//每一个function都有一个prototype属性,称之为显式原型,默认指向一个空的Object实例对象function F(){ //创建函数时,内部产生语句:this.prototype = {}}//为原型中添加test()方法F.prototype.test = function(){console.log('这里是为原型中添加的方法')}//每一个实例对象都有一个__proto__属性,称之为隐式原型var f = new F() //创建实例对象时,内部产生语句: this.__proto__ = F.prototype//实例对象的隐式原型的值和其构造函数显式原型的值所对应console.log(f.__proto__ === F.prototype) // true
</script>
在实际执行过程中,其内存图如下所示:
上述过程可以总结为:
- 函数的prototype属性:在定义函数时自动添加,默认值是一个空的Object实例对象。
- 实例对象的__proto__属性:在创建实例对象时自动添加,默认值为其构造函数的prototype属性值。
- 这里需要注意在ES6之前,程序员能直接对显式原型进行操做,但不能直接操作隐式原型,因为会涉及到一些安全问题。
3.原型链
由上述讲解可知,在访问一个实例化对象的属性或者方法时,会先在自身属性中查找,找到则返回;如果找不到会沿着自身__proto__所指向的位置查找。我们思考一个问题,所查找的属性或者方法在实例化对象沿着自身的__proto__属性中没有找到呢,它会直接返回没有找到这个结果还是说会继续沿着某条链查找。
这里我们提出Objecct原型对象中存在的方法:toString() ...
上述toString()方法每个函数的prototype属性都是拥有的,可是这与我们之前所说的”每个函数是都会含有一个prototype属性,它默认指向一个object空对象“不是冲突了吗。其实并不冲突,每个函数的prototype属性确实默认是指向一个空的Object实例化对象的,那么既然是指向空的Object实例化对象,我们第二小节提到的只要是实例化对象必然就会有__proto__属性,而且这个__proto__属性是与其构造函数 Obejct( ) 的prototype属性对应起来的。我们根据第2小节的JS代码就会有如下图所示的内存空间关系:
上图中Object函数对象的prototype原型对象中也是有__proto__属性的,且其值为null,所以这个原型对象就是原型链中的查找尽头。
<script type="text/javascript">//每一个function都有一个prototype属性,称之为显式原型,默认指向一个空的Object实例对象function F(){ //创建函数时,内部产生语句:this.prototype = {}}//为原型中添加test()方法F.prototype.test = function(){console.log('这里是为原型中添加的方法')}//每一个实例对象都有一个__proto__属性,称之为隐式原型var f = new F() //创建实例对象时,内部产生语句: this.__proto__ = F.prototype//实例对象的隐式原型的值和其构造函数显式原型的值所对应console.log(f.T()) // Uncaught TypeError: f.T is not a functionconsole.log(f.T) // undefined
</script>
这里我们需要解释一下当 F 的实例化 f 为什么沿着原型链查找T()时会报错 ”Uncaught TypeError: f.T is not a function“,而 f.T 却是undefined :因为原型链的尽头__proto__值为null,当实例化对象查找到原型链的尽头时,还没有找到要找到属性或者方法,就会得到原型链尽头__proto__的值null --->这里f.T()其实对应了null(),对于null()来说肯定报错f.T()不是一个函数;而f.T则由于原型链尽头__proto__的值为null,返回了undefined。
4.补充
我们最后还需要补充几点:
- 假设有一个函数function Foo(){ },它理所应当有prototype属性,那么它有没有__proto__属性呢?答案是有的,可是我们之前不是说只有实例化对象才会有__proto__属性吗?没错,这个说法肯定是没有丝毫问题的,那么我们按照逻辑推下去function Foo(){ } 一定是一个实例化的对象,其实对于这个函数是这样的:function Foo() { } ----> var Foo = new Function() 我们很明显的可以看出函数 Foo() 也是一个实例化的对象,所有它既有prototype属性也还有__proto__属性,且其隐式原型__proto__与function Function()的显式原型prototype所对应。
对于构造函数function Function()也是同样的产生方式: Function = new Function() 所以Function()的隐式原型__proto__是与Function()的显式原型prototype所对应的。
最后就是function Object(): Object = new Function() 所以Object函数对象也同样有隐式原型__proto__,且其__proto__与 Function()的显式原型prototype所对应。
最终我们原型链的图如下所示:
5.原型链总结
1.访问一个对象的属性/方法时:
· 先在自身属性/方法中查找,找到则返回。
· 如果没有找到,就会沿着__proto__这条链去查找,找到也会返回。
· 如果查到了原型链的尽头还是没有查找到,则返回undefined。
2.因为在实例化对象查找属性或者方法时总是沿着__proto__隐式原型链查找,所以原型链又称作隐式原型链。
3.原型链的作用:查找对象的属性或者方法。
一文读懂原型链 prototype和__proto__详解相关推荐
- 一文读懂XPath基本语法_XPath语法详解_XPath教程
因为最近在学习与整理有关python爬虫的文章,连带遇到XPath的使用,就顺便一起整理出来. XPath与自动化的关系 XPath是一门在XML文档中查找信息的语言,可用来在XML文档中对元素和属性 ...
- 一文读懂RTSP协议-【RTSP协议详解】
RTSP简介 RTSP(Real Time Streaming Protocol)是由Real Network和Netscape共同提出的如何有效地在IP网络上传输流媒体数据的应用层协议.RTSP对流 ...
- 【一文读懂】Spring Bean生命周期详解
Spring Bean的生命周期 1 容器初始化 BeanFactory容器初始化:包含容器创建,资源定位,载入,注册. ApplicationContext容器初始化:包含容器创建,资源定位,载入, ...
- 原型模式Prototype,constructor,__proto__详解
最近由于在找工作,又拿起<JavaScript高级程序设计>看了起来,从中也发现了自己确实还是有很多地方不懂,刚刚看到原型模式这里,今天终于搞懂了,当然,我也不知道自己的理解是否有错. 1 ...
- 一文读懂区块链技术,史上最全,最通俗
(来自公众账号:赛联信链) 区块链已经来到世界14年了,中国成为重大战略也三年了.你说大家都懂区块链?其实人人都迷迷糊糊是真的,都知道却又说不清.作为区块链教育从业者,给学员讲了无数次,今天我有义务再 ...
- 原型链prototype和__proto__
所有的 JavaScript 对象都会从一个 prototype(原型对象)中继承属性和方法. 显示原型和隐式原型:构造函数的显示原型用来存放函数对象,而实例对象的隐式原型等同于构函数的显示原型.所有 ...
- 【JS对象】打败JS原型、原型链大恶魔方法详解
文章目录 什么是对象? 什么是面向对象? 创建对象的方式 原型是什么? __proto__属性 constructor属性 原型链 函数的定义类型有哪些? 函数也是一个对象 完整的原型链 打败恶魔第一 ...
- 深入理解原型链与继承(详解JS继承原理)
文章目录 原型链与继承 new 关键字的执行过程 构造函数.实例对象和原型对象 原型链的概念及图解 第一层`__proto__`指向:实例对象 第二层`__proto__`指向:`Function.p ...
- 一文搞懂线程池原理——Executor框架详解
文章目录 1 使用线程池的好处 2 Executor 框架 2.1 Executor 框架结构 2.2 Executor 框架使用示意图 2.3 Executor 框架成员 2.3.1 Executo ...
最新文章
- DDD领域驱动设计之聚合、实体、值对象
- 莱比特矿池CEO江卓尔:BCH作为货币不需要新功能,但出于货币竞争的考虑需要
- [NOI2007]货币兑换Cash(DP+动态凸包)
- Eclipse安装hibernate插件的问题
- python文件选择:tkFileDialog 基础
- linux如何挂载U盘
- 布局中常见的居中问题
- cassandra写数据CommitLog
- Intellij IDEA 测试scala程序的时候: Test is already defined as object Test
- 快排第n趟排序结果校验
- Win2008使用无线网络
- 邯郸百亿斤粮食生产 国稻种芯·中国水稻节:河北大市粮食经
- 解决翻译论文时出现的换行问题-网页翻译-谷歌插件-翻译助手
- Alluxio的Raft HA实现
- Anaconda3+Tensorflow2.0(gpu)安装教程-小新Pro13英特尔独显版win10系统
- react在线浏览doc_如何实现 React 中的状态自动保存?
- Android Protect-0.重新打包和签名
- JAVA 页面置换先进先出算法(FIFO)
- AndroidStudio查找快捷键
- 为什么美国学生学的数学比我们简单,却能做出很牛逼的东西?