当前位置: 首页 > 编程日记 > 正文

Javascript - prototype、__proto__、constructor

最近看了很多文章,想要更通透的搞懂JS中的prototype__proto__constructor属性,从各个博主的文章里摘取了我认为可以有助于理解的一些内容,希望自己能够掌握好这一重要知识点的同时也帮助到大家,具体内容请见下文。

(注意:文中__proto__属性的两边是各由两个下划线构成。 )


本文通过下面一个简单的例子展开讨论,并配以相关的图帮助理解:

function Foo() {this.name="小红";
};	//创建一个函数Foo
var f1 = new Foo();	
var f2 = new Foo();		//通过new关键字,实例化Foo这个构造函数得到一个实例化对象f1console.log(f1 === f2); //false// 打印起来,看看都是啥吧
console.log(fn.prototype);	//undefined,可见由构造函数创建出的对象没有prototype属性,但是有__proto__属性
console.log(Foo.prototype);	//{constructor: ƒ Foo(...), __proto__: Object}
console.log(fn.__proto__);	//{constructor: ƒ Foo(...), __proto__: Object} fn.__proto__指向构造函数的prototype
console.log(Foo.__proto__);	//ƒ () { [native code] } 指向了Foo的原型对象Function,大家可以尝试一下Function.prototype是什么结果
console.log(fn.constructor);	//ƒ Foo() {this.name="小红"}	对象的constructor用于返回创建这个对象的函数(即构造函数)
console.log(Foo.constructor);	//ƒ Function() { [native code] } Foo也是一个对象,它的构造函数是Function(),所以返回的也是Function(),而Function()较特殊,它的构造函数是他自己,下文会再次强调
console.log(Foo.prototype.constructor);	//ƒ Foo() {this.name="小红"}
console.log(Foo.prototype.__proto__);	//{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ,…}
复制代码

虽然是简简单单的两行代码,然而它们背后的关系却是错综复杂的,如下图所示:

别怕,让我们一步步剖析,彻底搞懂它们。

图的说明:右下角为图例,红色箭头表示__proto__属性指向、绿色箭头表示prototype属性的指向、棕色箭头表示constructor属性的指向;蓝色方块表示对象,浅绿色方块表示函数。图的中间部分即为它们之间的联系,图的最左边即为例子代码

首先,我们需要牢记两点:

​ ① __proto__constructor属性是对象所独有的;

​ ② prototype属性是函数所独有的。

但是由于JS中函数也是一种对象,所以函数也拥有__proto__constructor属性,这点是致使我们产生困惑的很大原因之一。

(例如 获取图中Foo函数的__proto__和constructor的方式:Foo.prototype.__proto__; Foo.prototype.constructor;)

上图有点复杂,我们把它按照属性分别拆开,然后进行分析:

__proto__属性

__proto__属性,它是 对象所独有 的,可以看到__proto__属性都是由 一个对象指向一个对象 ,即指向它们的原型对象(也可以理解为父对象)。

那么__proto__属性的作用是什么呢?

它的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(可以理解为父对象)里找,如果父对象也不存在这个属性,则继续往父对象的__proto__属性所指向的那个对象(可以理解为爷爷对象)里找,如果还没找到,则继续往上找….直到原型链顶端 null (可以理解为原始人。。。),此时若还没找到,则返回undefined(可以理解为,再往上就已经不是“人”的范畴了,找不到了,到此为止),由以上这种通过__proto__属性来连接对象直到null的一条链即为我们所谓的 原型链

prototype属性

prototype属性,别忘了一点,就是我们前面提到要牢记的两点中的第二点:

​ ① 函数所独有的

​ ② 它是从一个函数指向一个对象

它的 含义函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象,由此可知:f1.__proto__ === Foo.prototype,它们两个完全一样。

prototype属性的作用又是什么呢?

它的作用就是 包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法

constructor属性

constructor属性也是对象才拥有的,它是从一个对象指向一个函数含义 就是指向该对象的构造函数,每个对象都有构造函数,从图中可以看出 Function 这个对象比较特殊,它的构造函数就是它自己(因为Function可以看成是一个函数,也可以是一个对象),所有函数最终都是由Function()构造函数得来,所以constructor属性的终点就是 Function()

——————————————————————

总结一下:

  1. 我们需要牢记两点:

    __proto__constructor属性是对象所独有的;

    prototype属性是函数所独有的,因为函数也是一种对象,所以函数也拥有__proto__constructor属性。

  2. __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象(父对象)里找,一直找,直到__proto__属性的终点null,然后返回undefined,通过__proto__属性将对象连接起来的这条链路即我们所谓的 原型链

  3. prototype属性的作用就是让该函数所实例化的对象们都可以找到公用的属性和方法,即f1.__proto__ === Foo.prototype

  4. constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象了)最终的构造函数都指向终点Function()

下面根据上述内容整理相关的表格如下:

__proto__constructorprototype
属性归属对象独有对象独有函数独有(由于函数也是对象,所以函数也拥有__proto__constructor属性)
含义指向它们的原型对象(也可以理解为父对象),最终指向null,实现了原型链指向该对象的构造函数,最终指向Function()函数的原型对象
指向一个对象指向一个对象一个对象指向一个函数一个函数指向一个对象
作用作为原型链的桥梁,帮助向上一层层找到被访问对象的属性,直到null返回对创建此对象的函数的引用包含可以由特定类型的所有实例共享的属性和方法,也就是让该函数所实例化的对象们都可以找到公用的属性和方法。

帮助理解

帮助理解 __proto__ 和 prototype

//如果你能耐心看完下列打印内容和注释,相信你会对 prototype、__proto__ 属性理解的更通透
function Fun(){};	//创造了一个函数Fun,这个函数由Function生成(Function作为构造函数)
var fn=new Fun();	//创建了一个函数fn,这个函数由Fn生成(Fn作为构造函数) 
console.log(fn.__proto__===Fun.prototype)    //true
// fn的__proto__指向其构造函数Fun的prototype
console.log(Fun.__proto__===Function.prototype)        //true
// Fun的__proto__指向其构造函数Function的prototype
console.log(Function.__proto__===Function.prototype)    //true
// Function的__proto__指向其构造函数Function的prototype
// 构造函数自身是一个函数,他是被自身构造的
console.log(Function.prototype.__proto__===Object.prototype)    //true
// Function.prototype的__proto__指向其构造函数Object的prototype
// Function.prototype是一个对象,同样是一个方法,方法是函数,所以它必须有自己的构造函数也就是Object
console.log(Fun.prototype.__proto__===Object.prototype)         //true
// 与上条相同
// 此处可以知道一点,所有构造函数的的prototype方法的__都指向__Object.prototype(除了....Object.prototype自身)
console.log(Object.__proto__===Function.prototype)        //true
// Object作为一个构造函数(是一个函数对象!!函数对象!!),所以他的__proto__指向Function.prototype
console.log(Object.prototype.__proto__===null)        //true
// Object.prototype作为一切的源头,他的__proto__是null// 下面是一个新的,额外的例子
var obj={}
// 创建了一个obj
console.log(obj.__proto__===Object.prototype)        //true
// obj作为一个直接以字面量创建的对象,所以obj__proto__直接指向了Object.prototype,而不需要经过Function了!!// 下面是根据原型链延伸的内容
// 还有一个上文并未提到的constructor,  constructor在原型链中,是作为对象prototypr的一个属性存在的,它指向构造函数(由于主要讲原型链,这个就没在意、);
console.log(obj.__proto__.__proto__ === null)        //true
console.log(obj.__proto__.constructor === Object)        //true
console.log(obj.__proto__.constructor.__proto__===Function.prototype)        //true
console.log(obj.__proto__.constructor.__proto__.__proto__===Object.prototype)    //true    
console.log(obj.__proto__.constructor.__proto__.__proto__.__proto__===null)        //true
console.log(obj.__proto__.constructor.__proto__.__proto__.constructor.__proto__===Function.prototype)    //true
复制代码

帮助理解 constructor和prototype

刚刚有说,constructor属性是对象才拥有的,它是从一个对象指向一个函数含义 就是指向该对象的构造函数

prototype属性是函数所独有的,是函数的原型对象,是由一个函数指向一个对象。

按照javascript的说法,function定义的方法就是一个Object(对象),而且还是一个很特殊的对象,这个使用function定义的对象与使用new操作符生成的对象之间有一个重要的区别。就是function定义的对象有一个prototype属性,使用new生成的对象就没有这个prototype属性

prototype属性又指向了一个prototype对象,注意prototype属性与prototype对象是两个不同的东西,要注意区别。在prototype对象中又有一个constructor属性,这个constructor属性同样指向一个constructor对象,而这个constructor对象恰恰就是这个function函数本身。

有点晕么?请看下图:

function Person(name){  this.name=name;  this.showMe=function(){  alert(this.name);  }  
};  
var one=new Person('js');  console.log(one.prototype);	//undefined  证明了one这个对象没有prototype属性
console.log(typeof Person.prototype);	//object  
console.log(Person.prototype.constructor);	//function Person(name) {...}; 
console.log(one.constructor);	//function Person(name) {...}; 
复制代码

巩固

练习:下面自己动手画一画这段代码的原型图:

function People(name) {this.name = name;this.sayName = function() {console.log('my name is:' + this.name);}
}People.prototype.walk = function() {console.log(this.name + 'is walking');
}var p1 = new People('小明');
var p2 = new People('小红');
复制代码

练习:创建一个 Car 对象,拥有属性name、color、status;拥有方法run,stop,getStatus

function Car(name, color, status) {this.name = name;this.color = color;this.status = status;
}
Car.prototype = {constructor : Car,run: function() {this.status = 'run';},stop: function() {this.status = 'stop';},getStatus: function() {console.log(this.status);}
}var car1 = new Car('BMW', 'red', 'stop');复制代码

参考

本文内容均参考自以下文章(以便追根溯源):

www.cnblogs.com/xiaohuochai…

blog.csdn.net/cc188688768…

www.cnblogs.com/libin-1/p/6…

developer.mozilla.org/zh-CN/docs/…

www.cnblogs.com/zjunet/p/45…

www.ibm.com/developerwo…


如若发现文中纰漏请留言,欢迎大家纠错,我们一起成长。

转载于:https://juejin.im/post/5c765cdce51d453f4142d25a

相关文章:

DOS下读取4GB内存

好文章我收集下起来 CPU上电后,从ROM 中的BIOS开始运行。 BIOS是处在内存的最顶端64KB(FFFF0000H),还是1MB之下的64KB(F0000H)处呢?事实上,BIOS在这两个地方都同时出现。 在保护模式…

7纳米duv和euv_要超车台积电 三星宣布采用EUV技术7纳米制程完成验证

在晶圆代工市场,台积电与三星的竞争始终是大家关心的戏码。三星虽然有高通等VIP客户,但在7纳米制程节点,高通预计会转投台积电,三星要想受更多客户的青睐,只能从制程技术着手了。这也是三星跳过非EUV技术的7纳米制程&a…

HDU 1711 Number Sequence(KMP算法)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid1711 Number Sequence Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 15548 Accepted Submission(s): 6836Problem DescriptionGiven two s…

分享45款高质量的免费(X)HTML/CSS模板

当你需要在短时间内设计出一个网站的时候,网站模板就非常有用了。这也就是为什么这些设计模板已成为设计领域的最新趋势的原因。在这篇文章中,收集了各式各样的网站模板,您可以免费下载使用,希望这些设计模板不仅带给您灵感&#…

运维开发笔记整理-前后端分离

运维开发笔记整理-前后端分离 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任。 一.为什么要进行前后端分离 1>.pc, app, pad多端适应 2>.SPA开发式的流行(单页Web应用(single page we…

初识mysql数据字段属性_MySQL数据库~~~~初识、基础数据类型

一 数据库初识1.1 什么是数据库数据库(DataBase,简称DB),简而言之可视为电子化的文件柜----存储电子文件的处所,用户可以对文件中的数据运行新增,截取,更新,删除等操作. 所谓数据库是以一定方式储存在一起,能予多个用户 共享,具有尽可能小的冗余度,与应用程序彼此独立的数据集合…

WinForm导出文件,你懂的……

好久没有写文章了,下面把自己最近程序中用到的一个小小的导出文件的方法给在家分享一下,欢迎大家来排砖,谢谢~不说废话了,直接上代码: 1 using System; 2 using System.Collections.Generic; 3 using System.Linq; …

PL/SQL第五章 Order by排序

1 -- 排序2 -- 1、列明排序3 -- 2、别名排序4 -- 3、列位置排序(当使用union,union all,intersect,minus集合操作,列明不同,但希望排序)5 SELECT deptno,dname FROM dept UNION6 SELECT empno,ename FROM emp7 ORDER BY 1 DESC;8 …

想转行学python过来人提醒大家几点

因为目前python非常火,应用也非常广泛,是目前最火的行业之一,竞争很大,工资很高,未来发展也极好。 首先告诉你,零基础学习python难度还是有的,python的专业程度本身就不简单,学习这事…

mysql答题表设计_PHP+MYSQL问答系统中的提问和回答的表怎么设计

展开全部PHPMYSQL 的问答系32313133353236313431303231363533e78988e69d8331333337396236统的设计与实现,问答系统简而言之 就是一个网上交流系统,针对学校这个特定环境,以学生和老师为主体,以实验室信息交流为话题而建立起的一个…

Android实时获取音量(单位:分贝)

基础知识 度量声音强度,大家最熟悉的单位就是分贝(decibel,缩写为dB)。这是一个无纲量的相对单位,计算公式如下: 分子是测量值的声压,分母是参考值的声压(20微帕,人类所能…

排序算法 - 堆排序

堆排序是指利用堆这种数据结构所设计的一种排序算法。 类型:选择排序时间复杂度(最坏):O(nlogn)时间复杂度(最好):O(nlogn)时间复杂度(平均):O(nlogn)空间复杂…

textContent与innerText的不同(转发)

textContent与innerText的不同 IE下有个innerText属性,FF下有个textContent属性。很多以前给IE写脚本的,在FF下找不到innerText属性,于是网上搜到的建议是用textContent来替代。反之给FF写脚本的也一样。 但是实际上,这里有个误解…

mysql插入性能_mysql 数据量大时插入和查询性能

现在mysql中有数据33.8w的数据,然后做查询和更新或插入操作,速度很慢,基本100条数据就要1.68s。好慢啊,我要测试一下,到底慢在哪?能不能提高点速度?参考一篇博文:http://blog.csdn.n…

Ext JS 4 笔记1

ExtJS4 引入了现在灰常流行的前端MVC。这在原本的3.3.1里面是没有的。原先项目里为了实现相对的MVC,自己写了一个controller和model ,收集并且保持JS端的数据。所以呢,这时候的文档结构就完全不一样了。原本的结构更像是传统 C# winform &…

activemq 消息阻塞优化和消息确认机制优化

一、消息阻塞优化 1.activemq消费者在从待消费队列中获取消息是会先进行预读取,默认是1000条(prefetch1000)。这样很容易造成消息积压。 2.可以通过设置prefetch的默认值来调整预读取条数,java代码如下 //设置预读取为1ActiveMQPr…

iOS-查询数据库--指定数据表中的当前数据行的总数量

很多时候,我们在查询一个表的时候,不想得到里面的记录内容,只是想简单的得到符合查询条件的记录条数。 FMDB中有一个很简单的方法就可以实现,见下面的代码实例: #import "FMdatabase.h" (int)numberOfCurre…

mysql 判断日期是否在某范围内_判断时间是否在某个区间内

private bool IsInTimeInterval(DateTime time, DateTime startTime, DateTime endTime) {//判断时间段开始时间是否小于时间段结束时间,如果不是就交换 if (startTime > endTime) {DateTime tempTime = startTime; startTime = endTime; endTime = tempTime; } //获取以公…

数据库索引-基本知识

为什么80%的码农都做不了架构师?>>> 数据库索引--基本知识 有许多因素会影响数据库性能。最明显的是数据量:您拥有的数据越多,数据库的速度就越慢。虽然有很多方法可以解决性能问题,但主要的解决方案是正确索引数据库…

Microsoft Enterprise Library 5.0 系列(八) Unity Dependency Injection and Interception

依赖注入容器Unity: Unity的构造类似于Castle中的IOC(控制反转 或者叫依赖注入)容器,我们使用抽象接口来隔离使用者和具体实现之间的依赖关系,但是不管再怎么抽象,最终还是要创建具体实现类的实例,这种创建具体实现类的…

pycharm 使用小结

1.pycharm 自动换行,显示行号,缩进向导 在代码右侧右键 2.自动注释/取消注释 ctrl /转载于:https://www.cnblogs.com/xuesu/p/4755086.html

golang socket读写同时_epoll在Golang的应用

使用Golang可以轻松地为每一个TCP连接创建一个协程去服务而不用担心性能问题,这是因为Go内部使用goroutine结合IO多路复用实现了一个“异步”的IO模型,这使得开发者不用过多的关注底层,而只需要按照需求编写上层业务逻辑。这种异步的IO是如何…

HTTP 2.0与OkHttp

HTTP 2.0是对1.x的扩展而非替代,之所以是“2.0”,是因为它改变了客户端与服务器之间交换数据的方式。HTTP 2.0增加了新的二进制分帧数据层,而这一层并不兼容之前的HTTP 1.x服务器及客户端——是谓2.0。  在正式介绍HTTP 2.0之前,…

根据“坐标”生成趋势图

数据库环境:SQL SERVER 2008R2 有一“坐标”表t,表结构如下: id int, num int 字段id是序号,递增且连续,字段num是数值类型。id可以看成是坐标轴的横轴,num则跟纵轴有关系&…

Winform程序怎么降低占用的内存?

1 Winform程序怎么降低占用的内存?winform程序占用的内存数一直居高不下,提供给用户的手册中说明内存不能大于50MB,但是每次运行的时候,内存都会飙高到100多MB. 2 3 后来终于发现了一个方法,可以解决这个问题: …

mysql关系表控制_mysql表关系

一、表的详细操作1.修改表名alter table 旧表名 rename 新表名;​2.修改表的引擎与字符编码alter table 表名 engine"引擎名" charset"编码名";​3.复制表 *#结构create table 新表名 like 旧表名;eg:1create table nt like tt;#将tt的表结构复制到新表nt中…

【Python3爬虫】常见反爬虫措施及解决办法(二)...

【Python3爬虫】常见反爬虫措施及解决办法(二) 这一篇博客,还是接着说那些常见的反爬虫措施以及我们的解决办法。同样的,如果对你有帮助的话,麻烦点一下推荐啦。 一、防盗链 这次我遇到的防盗链,除了前面说…

【原创】ListView快速滚动至新添加一行(自动滚动)

在C#开发中我们经常要开发一些日志系统,尤其是基于ListView的日志显示系统。但是当日志增多是你是否有一些困扰,就是它为什么不会自动滚动至最后一行。以下是一小段代码,希望可以帮助你. public void addLog(string logString) { lock (_lock…

MFC调用CFileDialog之后目录居然会改变,调试了好久终于发现是这个问题

MFC调用CFileDialog之后目录居然会改变,调试了好久终于发现是这个问题,上网搜了下,发现也有人和我出现相同的问题。他的博客如下: http://www.programlife.net/current-directory-changed-after-using-cfiledialog.html MFC调用C…

mysqlls_mysql基本命令

1、Mysql启动命令:命令行内容为:\>net start mysql运行情况如图1所示:图1(Mysql启动命令)2、连接Mysql服务器:命令行内容为:\>mysql -u root -h hostaddress -p password其中,root为Mysql的用户名&a…