本文深入探讨JavaScript原型链的核心机制,重点解析Function与Object在原型链中的特殊地位。通过__proto__与prototype的查找路径,阐明为何普通对象最终指向Object,而构造函数最终指向Function,并解释“万物皆对象”在原型链中的具体体现与逻辑差异。
__proto__
在阅读《JavaScript高级程序设计》第四版时,原型链是必须掌握的核心概念。此前我一直认为,只需牢记以下结论即可:
对象的隐式原型(__proto__)指向其构造函数的显式原型(prototype)。
这一规律适用于所有对象,通过控制台验证即可证实:
const str = new String("123")
str.__proto__ === String.prototype // true
const arr = new Array(["123"])
arr.__proto__ === Array.prototype // true
const obj = new Object()
obj.__proto__ === Object.prototype // true
const bl = new Boolean(false)
bl.__proto__ === Boolean.prototype // true
......
const fn = function(){}
fn.__proto__ === Function.prototype // true
尽管日常开发中我们更常使用字面量或构造函数声明,但无论声明方式如何,最终结果保持一致:
const str = '123'
str.__proto__ === String.prototype // true
const arr = [123]
arr.__proto__ === Array.prototype // true
const obj = {}
obj.__proto__ === Object.prototype // true
const bl = false
bl.__proto__ === Boolean.prototype // true
......
const fn = new Function()
fn.__proto__ === Function.prototype // true
基于此逻辑,若继续在构造函数本身查找__proto__,可得:
String.__proto__=== Function.prototype // true
Array.__proto__=== Function.prototype // true
Boolean.__proto__=== Function.prototype // true
Object.__proto__=== Function.prototype // true
Function.__proto__=== Function.prototype // true
所有基础构造函数(如String、Array、Boolean、Object等)实际上均由Function构造生成。
能否继续通过__proto__向上查找?答案是否定的,因为结果将陷入无限循环,始终指向同一行代码:
Function.__proto__ === Function.prototype
因此,无论通过__proto__如何向上追溯,最终只能到达Function,且Function的隐式原型等于其显式原型。
prototype.__proto__
但这似乎与“万物皆对象”的认知相悖。
若尝试进一步通过__proto__向前追溯,发现:
Function.__proto__.__proto__ === Object.prototype // true
Function.__proto__ === Function.prototype // true
Function.prototype.__proto__ === Object.prototype // true
作为终极构造函数的Function,其显式原型的隐式原型竟然指向Object的显式原型。
其他构造函数亦遵循相同规律,均可追溯至Object:
String.prototype.__proto__=== Object.prototype // true
Array.prototype.__proto__=== Object.prototype // true
Boolean.prototype.__proto__=== Object.prototype // true
Object.prototype.__proto__=== Object.prototype // true
所有构造函数的显式原型的隐式原型,均等于Object的显式原型。
理解
为何如此设计?
为何普通对象仅通过__proto__向上查找,最终只能到达Function?
为何构造函数通过__proto__查找,却能到达Object?
这是否意味着原型链存在两套不同的查找逻辑?
对此,一种通俗的理解方式如下:
【Function】如同创造万物的上帝,它创造了各类物质【对象】,物质进一步分化为:人【字符串】、鱼【数组】、鸟【布尔】、兽【数值】、石头【Date】、花草【正则】等类别;
这些不同类别的物质,再一代代繁衍延续。
问:这些类别及其后代的特性【属性】源自何处?
1. 可从祖先继承,符合生物遗传规律,如龙生龙、凤生凤;
let Mouse = function(){
this.makeAHole = true
}
let m1 = new Mouse()
m1.makeAHole // true
m1.__proto__.makeAHole === Mouse.prototype.makeAHole // true
2. 亦可从【物质】这一原始分类继承,因为人、鱼、鸟、兽、花草、石头均属于“物质”,拥有共同属性(如碳元素),正如字符串、数组、布尔、数值均属于“对象”,共享toString方法;
Object.prototype.carbon = true
let p1 = 'man'
p1.carbon // true
p1.__proto__.__proto__.carbon === Object.prototype.carbon// true
对象Object(物质)由函数Function(上帝)创造,逻辑成立。
上帝(Function)本身也是一种物质(Object),一切皆物质(Object),物质起源于大爆炸,源于空(null),逻辑亦成立。
参考经典图示:

基于上述理解绘制的示意图:

至此,对JavaScript原型链中函数与对象的关系理解暂告一段落,更多相关资料请关注本站其他文章。