从数组探寻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)
并没有发现用过的 reverse 和 toString 方法,而当我们展开 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 使用 reverse 和 toString 方法时,除了在自身层面和 prototype 中寻找以外,仿佛能够通过 __proto__ 链接到另一个地方并使用它的方法。大胆猜测一下:这些方法是不是 arr 的构造函数所有的呢?
我们知道,数组的构造函数是 Array ,所以有了以下验证
arr.reverse === Array.prototype.reverse // 返回true
果然 arr 中的 reverse 同时也在 Array 的原型 prototype 中,进一步细化,我们可以验证
arr.__proto__.reverse === Array.prototype.reverse // 返回true
为了更直观地表示,我们可以画一张图

由此可见, arr.reverse Array.prototype.reverse arr.__proto__.reverse 三者是等价的,对象的 __proto__ 属性链接到了构造函数的原型 prototype 上,此构造函数的 __proto__ 属性链接到了自己的构造函数的原型 prototype 上……
这个就是原型链
所有函数都有一个 .prototype 属性,对应一个对象
使用 new 函数创建的新对象都有一个.__proto__属性,指向构造函数的.prototype
当使用对象的属性或方法时,先从自身寻找,找不到再从.__proto__找,以此递进,直到.__proto__为 null
所以现在可以回答开头的问题了:
reverse 和 toString 等非自定义的方法或属性是在对象的原型链上定义的。
以此为依据可以进一步拓展,找一找对象的根源在哪里,也就是对象归根结底是由谁创建的。
这里可以使用运算符 instanceof 检测构造函数的 prototype 属性是否出现在某个实例对象的原型链上
arr instanceof Object // 返回true
Object instanceof Function //返回true
Function instanceof Function //返回true
验证可知, arr 对象是由 Object 函数创建的,而 Object 是由 Function 创建的,而 Function 也是由 Function 创建的。普通对象都是由 Object 函数创建的,函数都是由 Function 函数创建的。

浙公网安备 33010602011771号