JavaScript 原型与原型链详情


Posted in Javascript onNovember 02, 2021

前言:

JavaScript常被描述为一种「基于原型的语言」——每个对象都拥有一个「原型对象」,对象以其原型为模板、从原型继承属性和放法。原型对象也可能拥有原型,并从中继承属性和方法,一层一层以此类推。这种关系常被称为「原型链」,它解释了为何一个对象会拥有定义在其他对象中的属性和方法。

准确的说,这些属性和方法定义在Object的构造函数的prototype属性上,而非对象实例本身。

四句话道破原型与原型链:

  • 每个函数(类)天生自带一个属性prototype,属性值是一个对象,里面存储了当前类供实例使用的属性和方法 「(显示原型)」
  • 在浏览器默认给原型开辟的堆内存中有一个constructor属性:存储的是当前类本身(⚠️注意:自己开辟的堆内存中默认没有constructor属性,需要自己手动添加)「(构造函数)」
  • 每个对象都有一个__proto__属性,这个属性指向当前实例所属类的原型(不确定所属类,都指向Object.prototype)「(隐式原型)」
  • 当你试图获取一个对象的某个属性时,如果这个对象本身没有这个属性,那么它会去它的隐式原型__proto__(也就是它的构造函数的显示原型prototype)中查找。「(原型链)」

构造函数,原型与实例的关系:

每个构造函数(constructor)都有一个原型对象(prototype),原型对象(prototype)都包含一个指向构造函数(constructor)的指针,而实例(instance)都包含一个指向原型对象(__proto__)的内部指针

1、prototype(显式原型)

每个函数都有一个prototype属性

// 构造函数(类)
function Person(name){
    this.name = name
}
// new了一个实例 (对象)
var person = new Person('南玖')
console.log(person) //Person { name: '南玖' }
console.log(Person.prototype)  //构造函数(类)的原型 ----->对象
Person.prototype.age = 18  // 构造函数原型
console.log(person.age)  // 18

上面我们把这个函数Person的原型打印出来了,它指向的是一个对象,并且这个对象正是调用该构造函数而创建的实例的原型

JavaScript 原型与原型链详情

上面这张图表示的是构造函数与实例原型之间的关系,所以我们知道了构造函数的prototype属性指向的是一个对象。

那实例与实例原型之间的关系又是怎样的呢?这里就要提到__proto__属性了

2、__proto__(隐式原型)

从上面四句话中我们可以知道这是每一个Javascript对象(除null)都具有的一个属性,这个属性会指向该对象的原型(也就是实例原型)

因为在JavaScript中没有类的概念,为了实现类似继承的方式,通过__proto__将对象和原型联系起来组成原型链,的以让对象访问到不属于自己的属性。

那么我们就能够证明实例与实例原型之间的关系

console.log(person.__proto__)  //实例(对象)的原型--->对象

console.log(person.__proto__ === Person.prototype)  //实例的原型与构造函数的原型相等

JavaScript 原型与原型链详情

从上图我们可以看出实例对象与构造函数都可以指向原型,那么原型能不能指向构造函数或者是实例呢?

3、constructor(构造函数)

原型是没有属性指向实例的,因为一个构造函数可以创建多个实例对象;

从前面的四句话中我们知道「在浏览器默认给原型开辟的堆内存中有一个constructor属性」,所以原型也是可以指向构造函数的,这个属性就是「constructor

于是我们可以证明一下观点:

console.log(Person.prototype.constructor) //实例的显式原型的构造函数ƒ Person(name){this.name = name}
console.log(person.__proto__.constructor)  //实例的隐式原型的构造函数 ƒ Person(name){this.name = name}
console.log(person.__proto__.constructor === Person.prototype.constructor)//true 实例原型的构造函数与类的构造函数相等
console.log(Person === Person.prototype.constructor)  //true

JavaScript 原型与原型链详情

实例对象的__proto__是如何产生的?
我们知道当我们使用new 操作符时,生成的实例对象就拥有了__proto__属性

function Foo() {}
// 这个函数时Function的实例对象
// function是一个语法糖
// 内部其实调用了new Function()

所以可以说,在new的过程中,新对象被添加了__proto__属性并且链接到了构造函数的原型上。

4、new的原理

说简单点可以分为以下四步:

  • 新建一个空对象
  • 链接原型
  • 绑定this,执行构造函数
  • 返回新对象
function myNew() {
// 1.新建一个空对象
let obj = {}
// 2.获得构造函数
let con = arguments.__proto__.constructor
// 3.链接原型
obj.__proto__ = con.prototype
// 4.绑定this,执行构造函数
let res = con.apply(obj, arguments)
// 5.返回新对象
return typeof res === 'object' ? res : obj
}

5、原型链

说完了原型,我们再来看看什么是原型链?先来看一张图:

JavaScript 原型与原型链详情

这张图中,由__proto__串起来的链式关系,我们就称它为原型链

5.1 原型链的作用

原型链决定了JavaScript中继承的实现方式,当我们访问一个属性时,它的查找机制如下:

  • 访问对象实例属性,有的话直接返回,没有则通过__proto__去它的原型对象上查找
  • 原型对象上能找到的话则返回,找不到继续通过原型对象的__proto__查找
  • 一直往下找,直到找到Object.prototype,如果能找到则返回,找不到就返回undefined,不会再往下找了,因为Object.prototype.__proto__是null,说明了Object是所有对象的原型链顶层了。

从图中我们可以发现,所有对象都可以通过原型链最终找到 Object.prototype ,虽然 Object.prototype 也是一个对象,但是这个对象却不是 Object 创造的,而是引擎自己创建了 Object.prototype 。所以可以这样说,所有实例都是对象,但是对象不一定都是实例。

5.2 构造函数的__proto__是什么呢?

由上面的原型链的解释,我们应该能够理解构造函数的__proto__的,在JavaScript中所有东西都是对象,那么构造函数肯定也是对象,是对象就有__proto__

function Person(){}
console.log(Person.__proto__)
console.log(Function.prototype)
console.log(Person.__proto__===Function.prototype) // true

「这也说明了所有函数都是Function的实例」

那这么理解的话,Function.__proto__岂不是等于Function.prototype。。。。我们不妨来打印一下看看

Function.__proto__ === Function.prototype // true

打印出来确实是这样的。难道 Function.prototype 也是通过 new Function() 产生的吗?

答案是否定的,这个函数也是引擎自己创建的。首先引擎创建了 Object.prototype ,然后创建了 Function.prototype ,并且通过 __proto__ 将两者联系了起来。这里也很好的解释了上面的一个问题,为什么 let fun = Function.prototype.bind() 没有 prototype 属性。因为 Function.prototype 是引擎创建出来的对象,引擎认为不需要给这个对象添加 prototype 属性。

6、总结

  • Object 是所有对象的爸爸,所有对象都可以通过 __proto__ 找到它
  • Function 是所有函数的爸爸,所有函数都可以通过 __proto__ 找到它
  • Function.prototype Object.prototype 是两个特殊的对象,他们由引擎来创建
  • 除了以上两个特殊对象,其他对象都是通过构造器 new 出来的
  • 函数的 prototype 是一个对象,也就是原型
  • 对象的 __proto__ 指向原型, __proto__ 将对象和原型连接起来组成了原型链

到此这篇关于JavaScript 原型与原型链详情的文章就介绍到这了,更多相关JavaScript 原型与原型链内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
js实现动态添加、删除行、onkeyup表格求和示例
Aug 18 Javascript
如何设置一定时间内只能发送一次请求
Feb 28 Javascript
JavaScript极简入门教程(二):对象和函数
Oct 25 Javascript
javascript实现playfair和hill密码算法
Dec 07 Javascript
jquery获取select选中值的方法分析
Dec 22 Javascript
原生js实现密码输入框值的显示隐藏
Jul 17 Javascript
Vue中的ref作用详解(实现DOM的联动操作)
Aug 21 Javascript
p5.js 毕达哥拉斯树的实现代码
Mar 23 Javascript
javascript实现自由编辑图片代码详解
Jun 21 Javascript
vue element upload实现图片本地预览
Aug 20 Javascript
对vue中的事件穿透与禁止穿透实例详解
Oct 28 Javascript
实例讲解JavaScript 计时事件
Jul 04 Javascript
javascript实现计算器功能详解流程
JS创建或填充任意长度数组的小技巧汇总
Oct 24 #Javascript
一文彻底理解js原生语法prototype,__proto__和constructor
Oct 24 #Javascript
javascript遍历对象的五种方式实例代码
Oct 24 #Javascript
低门槛开发iOS、Android、小程序应用的前端框架详解
Oct 16 #Javascript
基于angular实现树形二级表格
ajax请求前端跨域问题原因及解决方案
You might like
php入门学习知识点八 PHP中for循环基本应用之九九乘法口绝表
2011/07/14 PHP
php中time()和mktime()方法的区别
2013/09/28 PHP
PHP Imagick完美实现图片裁切、生成缩略图、添加水印
2016/02/22 PHP
thinkphp 手机号和用户名同时登录
2017/01/20 PHP
PHP使用Redis替代文件存储Session的方法
2017/02/15 PHP
ThinkPHP 模板引擎使用详解
2017/05/07 PHP
PHP实现将多个文件中的内容合并为新文件的方法示例
2017/06/10 PHP
PHP基于双向链表与排序操作实现的会员排名功能示例
2017/12/26 PHP
javaScript arguments 对象使用介绍
2013/10/18 Javascript
jquery的ajax和getJson跨域获取json数据的实现方法
2014/02/04 Javascript
Jquery性能优化详解
2014/05/15 Javascript
textarea不能通过maxlength属性来限制字数的解决方法
2014/09/01 Javascript
用C/C++来实现 Node.js 的模块(二)
2014/09/24 Javascript
jQuery+canvas实现的球体平抛及颜色动态变换效果
2016/01/28 Javascript
js从外部获取图片的实现方法
2016/08/05 Javascript
微信小程序开发(二)图片上传+服务端接收详解
2017/01/11 Javascript
JS实现求数组起始项到终止项之和的方法【基于数组扩展函数】
2017/06/13 Javascript
详解Node全局变量global模块
2017/09/28 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
2019/05/17 Javascript
vue实现搜索功能
2019/05/28 Javascript
[48:21]林俊杰圣堂刺客超神杀戮秀
2014/10/29 DOTA
[03:19]2016国际邀请赛中国区预选赛第四日TOP10镜头集锦
2016/07/01 DOTA
[01:31:03]DOTA2完美盛典全回顾 见证十五项大奖花落谁家
2017/11/28 DOTA
pycharm配置pyqt5-tools开发环境的方法步骤
2019/02/11 Python
对python中url参数编码与解码的实例详解
2019/07/25 Python
Python super()方法原理详解
2020/03/31 Python
美国职棒大联盟官方网上商店:MLBShop.com
2017/11/12 全球购物
实习老师个人总结的自我评价
2013/09/28 职场文书
家长给小学生的评语
2014/01/30 职场文书
就业协议书
2014/09/12 职场文书
职工擅自离岗检讨书
2014/09/23 职场文书
2014年信息中心工作总结
2014/12/17 职场文书
交警失职检讨书
2015/01/26 职场文书
公司搬迁通知
2015/04/20 职场文书
2019安全宣传标语大全
2019/08/14 职场文书
详解Redis实现限流的三种方式
2021/04/27 Redis