前端小白,第一次写文章,主要用于记录收获,如有错误,还望大家指出。
当被问到原型链时,大部分小伙伴都会觉得这是送分题,自信答出:
- JS中万物皆对象,函数也属于对象。
- 所有对象都有__proto__属性,只有函数才有特殊的prototype属性。
- 函数实例的__proto__指向prototype。
那么原型链的终点在哪里呢,有些人(没错,就是面试时候的我🤣)可能就开始含糊其辞了。为了彻底搞懂原型链的终点,写篇文章记录一下。
1、Object函数的终点
对于一个普通的函数,它的终点是什么呢?
function Foo(){}
Foo.__proto__ === Function.prototype // 毫无疑问
Foo instanceof Function // true
Foo instanceof Object // true
typeof Foo.__proto__ // function
typeof Foo.prototype // object
可以看出,普通函数既有__proto__也有prototype。普通函数的__proto__
其实就是Function.prototype
,但是为什么typeof Foo.__proto__会是function?
我们稍后会讨论;那么普通函数的prototype是什么呢?由上面的代码可以看出,普通函数的prototype其实就是一个对象,我们输出看一下:
Foo.prototype.constructor === Foo // true
Foo.prototype.__proto__ === Object.prototype //true
可以得出结论,普通函数的prototype就是一个对象,它有两个属性
: 它的constructor
指向Foo自身,它的__proto__
指向Object.prototype
那么Object.prototype是啥嘞,输出看一下
理所当然的,Object.prototype
上挂载了我们平时用到的许多方法,这也是我们平时可以对实例调用那么多方法的原因啦。
let f1 = new Foo()
f1.toString()
在f1.toString()时,会在自身上查找toString,若在自身上找不到,则在f1.__proto__
上继续查找,也就是Foo.prototype
,若仍然找不到,则在f1.__proto__.__proto__
上继续查找,也就是Object.prototyoe
,
并且Object.prototype.__proto__ === null
就是Object的终点啦! 但是为什么要这么设计呢?理论上来说: 既然typeof Object.prototype === 'object'
,就说明Object.prototype应该是Object的实例
,即Object.prototype.__proto__ === Object.prototype
,但是仔细想想,这样就会出现一直访问__proto__而且访问不到终点的情况啦,所以ES规定:Object.prototype.__proto__ === null
。
2、Function函数
Function函数的__proto__与prototype
我们来回答上面遗留下的问题:为什么typeof Foo.__proto__会是function?
先来看两段代码:
Function.__proto__ === Function.prototype // true`
Object.__proto__ === Function.prototype // true
我们都知道实例的__proto__指向构造函数的prototype,
根据上面等式,我们可以得出:Function 和 Object 都是 Function的实例!
有人就问了:Object是Function的实例勉强可以接受,因为Object其实也是一个函数,但Function是Function的实例是什么鬼?这不就无限套娃了吗!
我们可以看到,在浏览器中输出Function.__proto__
与Function.prototype
,确实是一模一样的
好了,我们暂时不得不接受一点:Function既然是一个函数,那么它的构造函数理应就是Function
有了理论,我们大胆猜测,对于Function.__proto__
来说,既然typeof Function.__proto__ === 'function',那么它的__proto__也应该是Function
,也就是说 Function.__proto__.__proto__ === Function.prototype
应该是成立的!
我们来验证一下猜想是否成立:
很好,猜想不成立
。那么Function.__proto__.__proto__
到底是什么呢?
我们输出一下:
哦豁?这看着是不是很眼熟呢?这不就是Object.prototype
吗!
不信邪的我们来试验一下:
Function.prototype.__proto__ === Object.prototype // true
Function.__proto__.__proto__ === Object.prototype // true
好了,接下来给出结论:一般而言,一个"function"类型的对象,应该是由Function函数生成的,也就是Function.prototype.__proto__ === Function.prototype
才对,如果是这样的话,一直使用__proto__
寻找下去,没有尽头。所以Javascript规定,Function.prototype.__proto__ === Object.prototype
,Object.prototype.__proto__ === null
。也就是在原型链的终点处有2个特殊情况。
总结
- 对于一般的函数:
它的__proto__指向Function.prototype
它的prototype是一个对象,包含constructor以及__proto__,它的__proto__指向Object.prototype
- 对于Function函数
Function.__proto__ === Function.prototype
Function.__proto__.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
- 对于Object
Object.__proto__ === Function.prototype
Object.prototype.__proto__ === null
另外,根据 2、3结论,我们还可以推出以下等式成立
Object.__proto__ === Function.__proto__