从数组探寻JavaScript原型链

以一个最普通的数组为例

let arr = [1, 3, 2, 4]

调用arr.reverse() 可以让数组进行逆序排列

arr.reverse() // 此时arr变为[4, 2, 3, 1]

调用 arr.toString() 会将数组内容展示为字符串形式

arr.toString() // 此时控制台输出"4,2,3,1", arr不变

此时查看 arr 的属性,得到如下结果

> console.dir(arr)
  ▼Array(4)
     0: 4
     1: 2
     2: 3
     3: 1
     length: 4
    ▶︎__proto__: Array(0)

并没有发现用过的 reversetoString 方法,而当我们展开 arr 的 __proto__ 属性时,我们找到了上述两个方法,并且还发现了一大堆见过的没见过的方法也在里面

> console.dir(arr)
  ▼Array(4)
     0: 4
     1: 2
     2: 3
     3: 1
     length: 4
    ▼__proto__: Array(0)
     ▶︎concat: ƒ concat()
     ▶︎constructor: ƒ Array()
     …
     ▶︎reverse: ƒ reverse() // 我们用过的reverse
     …
     ▶︎toString: ƒ toString() // 我们用过的toString
     …
     ▶︎__proto__: Object // 又一个__proto__

arr 的 __proto__ 末尾还包含一个 __proto__ ,如果继续展开,里面已经不再包含另一个 __proto__ 了,似乎到这里就结束了。

回到之前的问题,当数组 arr 使用 reversetoString 方法时,除了在自身层面和 prototype 中寻找以外,仿佛能够通过 __proto__ 链接到另一个地方并使用它的方法。大胆猜测一下:这些方法是不是 arr 的构造函数所有的呢?

我们知道,数组的构造函数是 Array ,所以有了以下验证

arr.reverse === Array.prototype.reverse // 返回true

果然 arr 中的 reverse 同时也在 Array 的原型 prototype 中,进一步细化,我们可以验证

arr.__proto__.reverse === Array.prototype.reverse // 返回true

为了更直观地表示,我们可以画一张图
image
由此可见, arr.reverse Array.prototype.reverse arr.__proto__.reverse 三者是等价的,对象的 __proto__ 属性链接到了构造函数的原型 prototype 上,此构造函数的 __proto__ 属性链接到了自己的构造函数的原型 prototype 上……

这个就是原型链

所有函数都有一个 .prototype 属性,对应一个对象
使用 new 函数创建的新对象都有一个 .__proto__ 属性,指向构造函数的 .prototype
当使用对象的属性或方法时,先从自身寻找,找不到再从 .__proto__ 找,以此递进,直到 .__proto__ 为 null

所以现在可以回答开头的问题了:
reversetoString 等非自定义的方法或属性是在对象的原型链上定义的。

以此为依据可以进一步拓展,找一找对象的根源在哪里,也就是对象归根结底是由谁创建的。

这里可以使用运算符 instanceof 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上

arr instanceof Object // 返回true
Object instanceof Function //返回true
Function instanceof Function //返回true

验证可知, arr 对象是由 Object 函数创建的,而 Object 是由 Function 创建的,而 Function 也是由 Function 创建的。普通对象都是由 Object 函数创建的,函数都是由 Function 函数创建的。

posted @ 2021-05-17 17:10  曾经的点工  阅读(222)  评论(0编辑  收藏  举报