原型链的终点,Function,Object,你真的清楚了吗?

1,918 阅读3分钟

前端小白,第一次写文章,主要用于记录收获,如有错误,还望大家指出。

当被问到原型链时,大部分小伙伴都会觉得这是送分题,自信答出:

- 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其实就是一个对象,我们输出看一下:

QQ图片20220408112917.png

Foo.prototype.constructor === Foo // true
Foo.prototype.__proto__ === Object.prototype //true

可以得出结论,普通函数的prototype就是一个对象,它有两个属性 : 它的constructor指向Foo自身,它的__proto__指向Object.prototype

那么Object.prototype是啥嘞,输出看一下

QQ图片20220408124121.png

理所当然的,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,确实是一模一样的

QQ图片20220408110344.png

QQ图片20220408110402.png

好了,我们暂时不得不接受一点:Function既然是一个函数,那么它的构造函数理应就是Function

有了理论,我们大胆猜测,对于Function.__proto__来说,既然typeof Function.__proto__ === 'function',那么它的__proto__也应该是Function,也就是说 Function.__proto__.__proto__ === Function.prototype 应该是成立的!

我们来验证一下猜想是否成立:

image.png

很好,猜想不成立。那么Function.__proto__.__proto__到底是什么呢? 我们输出一下:

image.png

哦豁?这看着是不是很眼熟呢?这不就是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个特殊情况。

总结

  1. 对于一般的函数:
它的__proto__指向Function.prototype
它的prototype是一个对象,包含constructor以及__proto__,它的__proto__指向Object.prototype
  1. 对于Function函数
Function.__proto__ === Function.prototype
Function.__proto__.__proto__ === Object.prototype
Function.prototype.__proto__ === Object.prototype
  1. 对于Object
Object.__proto__ === Function.prototype
Object.prototype.__proto__ === null

另外,根据 2、3结论,我们还可以推出以下等式成立

Object.__proto__ === Function.__proto__

彻底搞懂Function,Object,proto,prototype之间的关系 - 掘金 (juejin.cn)