__proto__和prototype之区别和联系

图标控
• 阅读 7044

学到原型的时候感觉头都大了/(ㄒoㄒ)/~~ 尤其是prototype和__proto__ 傻傻分不清,通过多番查找资料,根据自己的理解,记录下最近研究对象的一些心得,做一个记录与总结,以加深自己的印象,同时,希望也能给其他学JavaScript同胞一点启发。

在JavaScript中,万物皆对象。咱们写一个JavaScript对象,大多数时候是用构造函数创建一个对象或者用对象字面量创建一个对象。比如:

//通过构造函数来创建对象
function Person() {
    //...
}
const person1 = new Person();
//通过对象字面量创建对象
const person2 = {
    name: 'jessica',
    age: 27,
    job: 'teacher'
}

当然还有其他方式创建对象,这里就不列举出来了。那么问题来了,通过不同的方式创建的对象有什么区别呢?
我们知道,每个JS对象一定对应一个原型对象,并从原型对象继承属性和方法。那么对象是怎么和这个原型对象对应的呢?带着问题慢慢看下面的内容吧

__proto__和prototype概念区分

其实说__proto__并不准确,确切的说是对象的[[prototype]]属性,只不过在主流的浏览器中,都用__proto__来代表[[prototype]]属性,因为[[prototype]]只是一个标准,而针对这个标准,不同的浏览器有不同的实现方式。在ES5中用Object.getPrototypeOf函数获得一个对象的[[prototype]]。ES6中,使用Object.setPrototypeOf可以直接修改一个对象的[[prototype]]。为了方便,我下面的文章用__proto__来代表对象的[[prototype]]。

而prototype属性是只有函数才特有的属性,当你创建一个函数时,js会自动为这个函数加上prototype属性,值是一个空对象。所以,函数在js中是非常特殊的,是所谓的一等公民。
那么__proto__和prototype是怎么联系起来的呢?让我们来看下下面的代码:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
const person1 = new Person('jessica', 27);

当我们new Person()的时候到底发生了什么?
new一个构造函数,相当于实例化一个对象,这期间其实进行了这三个步骤:

创建对象,设为o,即: const o = {};
上文提到了,每个对象都有__proto__属性,该属性指向一个对象,这里,将o对象的__Proto__指向构造函数Person的原型对象(Person.prototype);
将o作为this去调用构造函数Person,从而设置o的属性和方法并初始化。
当这3步完成,这个o对象就与构造函数Person再无联系,这个时候即使构造函数Person再加任何成员,都不再影响已经实例化的o对象了。
此时,o对象具有了name和age属性,同时具有了构造函数Person的原型对象的所有成员,当然,此时该原型对象是没有成员的。

现在大家都明白了吧,简单的总结下就是:
js在创建对象的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype
那么一个对象的__proto__属性究竟怎么决定呢?答案显而易见了:是由构造该对象的方法决定的。

创建对象的不同方法解析

下面讲解三种常见的创建对象方法。

对象字面量

比如:

const Person = {
    name: 'jessica',
    age: 27
}

这种形式就是对象字面量,通过对象字面量构造出的对象,其__proto__指向Object.prototype。
所以,其实Object是一个函数也不难理解了。Object、Function都是是js自带的函数对象。
可以跑下面的代码看看:

console.log(typeof Object); 
console.log(typeof Function);

构造函数

就如我前面讲的,形如:

function Person(){}
const person1 = new Person();

这种形式创建对象的方式就是通过构造函数创建对象,这里的构造函数是Person函数。上面也讲过了,通过构造函数创建的对象,其__proto指向的是构造函数的prototype属性指向的对象。

Object.create

const person1 = {
    name: 'jessica',
    age: 27
}
const person2 = Object.create(person1);

这种情况下,person2的__proto__指向person1。在没有Object.create函数的时候,人们大多是这样做的:

Object.create = function(p) {
    function f(){};
    f.prototype = p;
    return new f();
}

一看大家就会明白了。

总结

其实仔细思考下上面提到的三种创建对象的方法,追究其本质,不难发现,最根本的还是利用构造函数再通过new来创建对象。所谓的对象字面量也只不过是语法糖而已,本质上是const o = new Object(); o.xx = xx;o.yy=yy;。 所以,函数真不愧是js中的一等公民呀~

原型链

既然已经提到了原型,就不得不提一下原型链了,毕竟这是实现继承最关键所在,也是js对象精妙所在。
还记得上文提到的一个总结吗?不记得?没关系,我贴出来让大家温故而知新,哈哈~
js在创建对象的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype而原型链的基本思想就是利用原型让一个引用类型继承另一个引用类型的属性和方法。
让我们再简单回顾下构造函数、原型和实例的关系:
每个构造函数都有一个原型对象,原型对象包含一个指向构造函数的指针(constructor),而实例则包含一个指向原型对象的内部指针(__proto__)。
我们拿一个例子来讲解:

function Person(name, age) {
    this.name = name;
    this.age = age;
}
const person1 = new Person('jessica', 27);

一图胜前言,我们用画图的形式来讲解下上面的例子:
__proto__和prototype之区别和联系

从上图可以看到,其实原型链的顶端是Object.prototype.__proto__,也即为null。

总结

函数是js中的一等公民,js在创建对象的时候,都有一个叫做__proto__的内置属性,用于指向创建它的函数对象的原型对象prototype。只有函数有prototype, 当你创建一个函数时,js会自动为这个函数加上prototype属性,值是一个空对象。

点赞
收藏
评论区
推荐文章
blmius blmius
4年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
4年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Karen110 Karen110
4年前
一篇文章带你了解JavaScript日期
日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用
Jacquelyn38 Jacquelyn38
4年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Souleigh ✨ Souleigh ✨
5年前
Js中 constructor, prototype, __proto__ 详解
本文为了解决以下问题:__proto__(实际原型)和prototype(原型属性)不一样!!!constructor属性(原型对象中包含这个属性,实例当中也同样会继承这个属性)prototype属性(constructor.prototype原型对象)__proto__属性(实例指向原型对象的指针)<br/首先弄清楚几个概念:<br/
Wesley13 Wesley13
4年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Stella981 Stella981
4年前
JavaScript Prototype
定义和用法prototype属性使您有能力向对象添加属性和方法。实例在本例中,将展示如何使用prototype属性来向对象添加属性:<scripttype"text/javascript"functionemployee(name,job,born){this.n
Wesley13 Wesley13
4年前
JS原型、原型链深入理解
原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对象有”constructor”属性。原型是JavaScript中一个比较难理解的概念,原型相关的属性也比较多,对象有”prototype”属性,函数对象有”prototype”属性,原型对