作为一名前端开发者,我们都知道JS是单继承的,而Object.prototype是原型链的顶端,所有对象从它继承了包括toString()、valueOf()等等公共属性。
鸡和蛋问题的由来
首先Object
和Function
都是构造函数,而所有的构造函数都是Function
的实例对象。 因此Object
是Function
的实例对象;而Function.prototype
是Object
的实例对象。所以这里就引伸出了一个有意思的鸡和蛋的问题:
Object instanceof Function // trueFunction instanceof Object // trueObject.__proto__ === Function.prototype // trueFunction.__proto__ === Function.prototype // trueFunction.prototype.__proto__ === Object.prototype // true复制代码
那么Object
和Function
,谁是鸡谁是蛋呢?
接下来就来深入探究下上面这段代码所引起的鸡生蛋蛋生鸡问题,从下面这张原型/原型链经典图入手,在这个过程中深入了解 Object.prototype
、Function.prototype
、function Object()
、function Function()
之间的关系,这个过程可能有点烧脑,毕竟是JS的一大玄学嘛。
Object.prototype
原型链的尽头就是Object.prototype
(不考虑 null
的情况下)。所有对象均从Object.prototype
继承toString() 等公共属性。
Object.prototype
表示 Object
的原型对象,实际上Object.prototype
并不是通过Object
函数创建的,为什么呢?看如下代码:
function Dog() { this.name = '川普';}var dog = new Dog();dog.__proto__ === Dog.prototype; // true复制代码
实例对象的__proto__
会指向构造函数的prototype
,即dog.__proto__
指向 Dog.prototype
,但是Object.prototype.__proto__
又是 null
,所以 Object.prototype
并不是通过 Object
函数创建的,那它如何生成的?其实 Object.prototype
是浏览器底层根据 ECMAScript 规范创造的一个对象,所以在经典图里面只是看起来Object.prototype
是通过 Object
函数创建的,实际上并不是。
Function.prototype
Function.prototype
和Function.__proto__
为同一对象。
这也意味着:Object
/Array
等等构造函数本质上和Function
一样,均继承于Function.prototype
,从经典图上来看都是通过new Function
构造出来的。
当然,Function.prototype
对象是一个函数(对象),其__proto__
属性指向 Object.prototype
,即Function.prototype
会直接继承root(Object.prototype
)。
通过这点我们可以弄清继承的原型链:Function|Object|Array...--->Function.prototype--->Object.prototype(root)
。如下图所示:
function Object()
Object
作为构造函数时,其__proto__
属性指向 Function.prototype
,即:
Object.__proto__ === Function.prototype // true复制代码
从经典图来看:
使用 new Object()
创建实例对象o1时,实例对象o1的 __proto__
属性指向构造函数的 prototype
属性,对应上图就是 Object.prototype
,即o1.__proto__ === Object.prototype
结果为true
。
Function.prototype
指向的对象,它的__proto__
会指向Object.prototype
,因为Function.prototype
指向的对象也是一个普通的被Object
创建的对象,所以也遵循基本的规则。
function Function()
Function
也是一个函数对象,也有__proto__
属性,既然是函数,那么它一定是被Function
创建,所以Function
是被自身创建的,所以它的__proto__
指向了自身的Prototype
:
Function.__proto__ === Function.prototype // true复制代码
到这里就有点烧脑了吧,我们再看下鸡生蛋蛋生鸡问题。
Function & Object 鸡和蛋问题
由上面可知,Object
构造函数继承了Function.prototype
,同时Function
构造函数继承了Object.prototype
,这里就产生了鸡和蛋的问题。为什么会出现这种问题呢?必须首先更深入一层去理解Function.prototype
这个对象,因为它是导致Function instanceof Object
和Object instanceof Function
都为true
的原因。
// Object instanceof Function 即Object.__proto__ === Function.prototype // true// Function instanceof Object 即Function.__proto__.__proto__ === Object.prototype // true// Object instanceof Object 即 Object.__proto__.__proto__ === Object.prototype // true// Function instanceof Function 即 Function.__proto__ === Function.prototype // true复制代码
根据JS规范,Function.prototype
又是个不同于一般函数(对象)的函数(对象),其中:
Function.prototype
像普通函数一样可以调用,但总是返回undefined
。- 普通函数实际上是
Function
的实例,即普通函数继承于Function.prototype
。即func.__proto__ === Function.prototype
。 Function.prototype
继承于Object.prototype
,并且没有prototype
这个属性。- 所以,
Function.prototype
其实是个另类的函数,可以独立于/先于Function
产生。
而Object
本身是个(构造)函数,是Function
的实例,即Object.__proto__
就是Function.prototype
。
总结:先有Object.prototype
(原型链顶端),Function.prototype
继承Object.prototype
而产生,最后,Function
和Object
和其它构造函数继承Function.prototype
而产生。
看到这里估计也都看烦了,是不是还是有点混乱呀?乱也很正常。那这篇文章就先让它乱着,下一篇我们将请另一个老朋友来帮忙,把它彻底理清楚,这位老朋友就是——instanceof
,那就且听请下回分解咯。
如果觉得文章对你有些许帮助,欢迎在点赞和关注,感激不尽!