一文读懂JavaScript原型链

开发 前端
对象通过隐式原型( __proto__​ )属性指向其构造函数的原型对象( prototype​ ),进而通过原型对象( prototype​ )的隐式原型( __proto__​ )属性指向更高层级的原型对象( prototype​ ),最终指向null而停止所形成的链条,则称其为原型链。

前言

什么是原型链

每个对象(Object)都有一个私有属性指向另一个名为原型(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链(prototype chain)中的最后一个环节。

说明

  1. __proto__实际为[[Prototype]]属性的访问器,为了便于理解,本文以属性代称其访问器实质
  2. 这里不使用class表达式是因为class表达式实际上是特殊的函数,更类似于将多个操作融合后的语法糖。使用Function来理解更为直观
  3. 所有代码均已在Chrome浏览器v125.0.6422.142经过结果验证

一、名词解释

在开始了解原型链之前,先介绍两个名词prototype以及__proto__,举一个简单的例子更为直观

定义一个函数Foo,而后创建一个Foo的实例对象o1。

function Foo(){}
const o1 = new Foo()

(一)、原型对象(prototype)

[!NOTE]

所有的函数都是对象,拥有独有属性prototype

prototype原型对象,是函数的独有属性。该属性指向一个对象。当函数(如:Foo)被实例化成一个对象(如:o1)后,实例对象(o1)可以访问到函数(Foo)的原型对象(prototype)

如:在Foo的prototype上增加属性propA,其值为'p1',可以发现在o1上也可以获取到属性propA,其值同样为'p1'

Foo.prototype.propA = 'p1'
o1.propA // 'p1'

那么o1是如何获取到Foo.prototype上的方法的呢,这就要介绍另一个概念,隐式原型__proto__

(二)、隐式原型(__proto__)

[!Note]

所有非内置对象都是函数的实例,拥有独有属性__proto__

__proto__隐式原型,是对象的独有属性。对象(如:o1)的__proto__属性指向其构造函数(如:Foo)的原型对象( prototype )。所有非内置对象都是函数的实例,同时拥有一个构造函数。如:对象o1的构造函数为Foo

内置对象如:Function、Date、Array、Object、Math、JSON等。

如:o1上访问到的属性propA实际上是其构造函数Foo的prototype的属性propA。这里将Foo.prototype.propA设置为一个对象,来防止因基本类型的值比较方式导致结论误差

o1.constructor === Foo // true

Foo.prototype.propA = {}
o1.propA // {}

o1.propA === o1.__proto__.propA // true
o1.__proto__.propA === Foo.prototype.propA // true

二、原型链

在介绍何为prototype以及__proto__后,接下来就开始介绍原型链了。仍以之前使用的例子来进行原型链的介绍

function Foo(){}
const o1 = new Foo()

(一)、实例化关系

首先一起来分析例子的实例化关系。可在Chrome中测试如下代码

o1.constructor === Foo // true
Foo.constructor === Function // true
Function.constructor === Function // true

根据验证结果可分析出如下图结论

图片图片

  1. o1的数据类型是对象,是函数Foo的实例化
  2. Foo的数据类型既是函数也是对象,也是Function的实例化
  3. Function既是函数也是对象,其是自身Function的实例化

(二)、独有属性分析

在实例化关系的基础上,继续分析每一级的属性关系。其中绿色代表函数独有属性,红色代表对象独有属性

图片图片

  1. o1的数据类型为对象,拥有独有属性__proto__,由于prototype是函数独有属性,所以o1上的prototype为undefined
  2. Foo的数据类型既是函数也是对象,所以其同时拥有属性prototype和__proto__
  3. Function的数据类型既是函数也是对象,所以其同时拥有属性prototype和__proto__

(三)、隐式原型引用关系

[!NOTE]

对象的隐式原型(__proto__)属性指向其构造函数(constructor)的原型对象(prototype)

图片图片

  1. o1.__proto__指向其构造函数Foo的原型对象( prototype )
  2. Foo.__proto__的指向其构造函数Function的原型对象( prototype )
  3. 由于Function的构造函数是其自身,所以Function.__proto__指向其自身的原型对象( prototype )

由于函数的原型对象( prototype )属性的数据类型为对象,因此同样具有对象的独有属性__proto__。如下图

图片图片

默认情况下,对象隐式原型( __proto__ )指向其构造函数的原型对象( prototype ),那么Foo.prototype和Function.prototype的构造函数有指向哪里呢?

[!NOTE]

所有函数的原型对象( prototype )的构造函数均指向其自身

可通过以下测试代码进行验证

Foo.prototype.constructor === Foo // true
Function.prototype.constructor === Function // true

内置函数对象的原型对象( prototype ),其隐式原型( __proto__ )也指向其自身

RegExp.prototype.constructor === RegExp // true
Date.prototype.constructor === Date // true
Map.prototype.constructor === Map // true
Array.prototype.constructor === Array // true
Number.prototype.constructor === Number // true
Object.prototype.constructor === Object // true

然而一些内置对象由于其没有函数特征,所以其原型对象( prototype )属性为undefined,其自身的constructor指向Object。

Math.prototype // undefined
Math.constructor === Object // true

JSON.prototype // undefined
JSON.constructor === Object // true

言归正传,由于所有函数的原型对象( prototype )的构造函数均为其自身,则如若Foo.prototype.__proto__指向其构造函数的prototype,即Foo.prototype.__proto__指向Foo.prototype。那么原型链的查找将进入无限循环。为了避免这个问题,则将所有函数原型对象( prototype )的隐式原型(__proto__)均指向Object.prototype。

图片图片

可通过以下代码进行验证:

Foo.prototype.__proto__ === Object.prototype // true
Function.prototype.__proto__ === Object.prototype // true

这里欠缺的有两个点:

  1. Object既是函数也是对象,所以其拥有对象的独有属性( __proto__ ),那么其隐式原型( __proto__ )指向哪里
  2. Object.prototype的数据类型为一个对象,如果其隐式原型( __proto__ )仍指向Object.prototype,那么原型链的查找将进入无限循环,那么其指向哪里

图片图片

针对第1点,Object自身既是函数又是对象,其作为对象的独有属性隐式原型( __proto__ )应指向其构造函数的原型对象( prototype )。Object对象的构造函数为Function,所以其隐式原型( __proto__ )指向Function.prototype。

可通过以下测试代码进行验证:

Object.constructor === Function // true
Object.__proto__ === Function.prototype // true

因此其指向关系如下图:

图片图片

针对第2点,文章起始什么是原型链已经给出了定义。

原型对象也有一个自己的原型,层层向上直到一个对象的原型为 null

因此其最终指向为null

图片图片

三、总结:

对象通过隐式原型( __proto__ )属性指向其构造函数的原型对象( prototype ),进而通过原型对象( prototype )的隐式原型( __proto__ )属性指向更高层级的原型对象( prototype ),最终指向null而停止所形成的链条,则称其为原型链。

责任编辑:武晓燕 来源: 大转转FE
相关推荐

2021-08-04 16:06:45

DataOps智领云

2023-12-22 19:59:15

2019-12-17 08:16:04

JavaScriptthis编程

2020-06-23 08:41:47

JavaScript开发技术

2018-09-28 14:06:25

前端缓存后端

2022-09-22 09:00:46

CSS单位

2022-11-06 21:14:02

数据驱动架构数据

2018-03-17 09:00:21

大数据 区块链

2022-10-20 08:01:23

2023-05-20 17:58:31

低代码软件

2023-11-27 17:35:48

ComponentWeb外层

2022-12-01 17:23:45

2021-12-29 18:00:19

无损网络网络通信网络

2022-07-26 00:00:03

语言模型人工智能

2022-07-05 06:30:54

云网络网络云原生

2024-01-03 08:54:17

Kubernetes策略工具

2021-05-18 09:48:58

前端开发架构

2018-10-30 11:10:05

Flink数据集计算

2017-05-04 20:29:12

HTTP服务器TCP

2021-02-05 05:26:33

字节ASCII控制
点赞
收藏

51CTO技术栈公众号