prototype

Prototype

蓝色 = 函数,
绿色 = 函数作为对象时的属性,
粉色 = 构造函数的实例,
橙色 = Object构造函数的实例,也就是我们常用的真正意义上的对象(比如 var a = {}; var b = new Object() 中的 a 和 b )

一、解释

1. 函数既是函数,也是对象,它具备两面性。函数的对象性质在图中用绿色表示。
2. 函数既然也是对象,那么它就可以直接定义属性或方法。

每个函数默认都具有两个属性,name(函数名) 和 length(参数个数)
比如

js
function Person() {}; console.log(Person.name) // 'Person';

也可以直接定义属性和方法,比如

js
Person.xx = 100;
3. 只有函数默认具备 prototype 属性,用来指向原型对象。其他普通对象是没有的。
4. 只有对象默认具备 __proto__属性,用来指向构造出这个对象的构造函数的 prototype 。

比如

js
function A(){}; A.prototype = {name:'a'}; var a = new A(); a.__proto__ === A.prototype // true

这样设计有什么用?

实现继承。

比如a对象本身是没有name属性的,但是当你访问a.name时却会得到'a',原因就是访问对象属性时,如果当前对象没有这个属性,会访问a.__proto__.name,如果有就返回这个值,如果没有,则继续访问a.__proto__.__proto__.name的有没有这个属性,一直找到__proto__为null时,返回一个undefined,结束。

5. 函数既然也是对象

那么它就同时拥有了prototype 和 __proto__ 两个属性!普通对象只有__proto__属性,可以认为prototype 是原型,__proto__ 是原型链。

二、分析

问题1:Object.prototype 是由Object构造出来的吗?

我个人认为,是的。

理由就是,如果我是作者,我会在在JS解释器初始化时做这样的事:

先用Function构造Function自己,
然后用Function构造一个Object,也就是 Object = new Function(), 此时Object就是一个构造函数了
再执行 Object.prototype = null;
然后执行 o = new Object, 然后给o挂在例如 asgin、keys等静态方法;
此时的 o.__proto__ 为 null, 因为new出来的对象的 __proto__ 指向构造函数的原型prototype。
最后执行Object.prototype = o,

这也就是为什么原型链顶端指向了null。这都是我的猜测。

再者说,Object.prototype.__proto__ 如果非要有指向,那就只能指向自己,因为一个对象的 __proto__ 要等于这个对象的构造函数的prototype,那么 Object.prototype 这个对象明显也是Object这个构造函数构造的,所以 Object.prototype.__proto__ 按理说应该等于Object.prototype

但是如果这样,原型链按照__proto__一层层查找属性到达底层就会出现死循环,这肯定不行,所以Object.prototype.__proto__ 只能指向null来杜绝这种问题。

问题2:为什么 Function.__proto__ 指向了 Function.prototype ?

首先,Function.prototype 指向了一个对象,严格来说,是指向了一个函数,虽然prototype指向一个函数是特殊了一点,但是这个没啥问题,因为毕竟函数也是对象。

其次,要理解 Function 构造了它自己这句话:
Function 构造出了 Function,那么 Function.__proto__ 就等于 Function.prototype,因为一个对象的 __proto__ 要等于这个对象的构造函数的prototype,所以,Function.__proto__ 指向了 Function.prototype 也是合情合理的。

问题3:为什么 AsyncFunction.prototype.__proto__ 指向了 Function.prototype? 不是应该指向Object.prototype 吗?

作者强行修改了这个属性!!