Appearance
__ proto __ & prototype
首先我们要知道,在JS代码没有执行时,环境中已经创建了
window
对象,其中具有构造函数window.Object()
和window.Array()
以及window.Number()
等。
这些构造函数首先具备构造对象的能力,同时又天生注册了许多功能方法,如 Object 的valueOf()
, Array 的push()
, Number的toFix()
等。这些方法都存在prototype
属性中,存放在系统内存中。
__proto__
是对象的属性, prototype
是构造函数的属性。
当对象调用方法时,在本身未查找到,会查找其构造函数的方法,具体如下。
举例
看创建对象的例子
ts
const obj = {}
obj.toString()
obj 上并不存在 toString()
方法,因为创建时,仅为空对象,但仍可以调用,是因为原型链查找,搜索到obj.__proto__.toString()
。
obj.__proto__
正是指向 window.Object.prototype
内存区,所以可以调用其中的方法。
再看创建数组的例子
ts
const arr = []
arr.push(1)
arr.toString()
同样的,arr 创建时,仅为空数组,且不带有方法,arr.push()
来自于 arr.__proto__
指向 window.Array.prototype
的内存区,存储有 push、pop 等数组方法。
而调用 arr.toString()
时,在上述数组方法内存中不具备 toString() ,因此按原型链查找,到 arr.__proto__.__proto__
,即 window.Array.prototype.__proto__
,指向 window.Object.prototype
内存区,调用该方法。
因此我们发现
window.Object.prototype
是链的起始段,是所有对象上的方法查找的终点。起始段意味着无法再继续查找,即window.Object.prototype.__proto__
为null
设想
想象一下,如果没有 prototype
,那么就没有共享方法的内存区域,就不存在原生的 toString、push、pop 等一切非用户自定义方法。
如果没有 __proto__
,对象方法的查找将无所下手,只能通过自定义引用到原型(构造函数)中的方法,如下
ts
const obj = {
toString: window.Object.prototype.toString,
valueOf: window.Object.ptototype.valueOf
}
obj.toString() // '[object Object]'
总结
prototype
是构造函数用来查找本身方法的属性, __proto__
是实例对象用来查找其原型上方法的属性。