Skip to content

原型链是每个前端工程师必备的技能,这里面的关键在于能否把整个原型链的关系图画出来。原型链的关系图是不需要刻意去背的,只要按照逻辑去推导,一步一步就画出来了。

对象怎么来的?

首先,所有的对象都是通过new关键词创建出来的,包括我们用的最多的写法:

js
const obj = { name: 'abc' };

虽然没有使用到new关键词,但实际也是通过new Object()创建出来的对象。


假设现在我们有一个构造函数Foo,然后通过Foo创建一个对象:

js
function Foo() {};

const obj = new Foo();

重点: 在JS中,函数也是对象,并且函数本身都会带有一个原型对象prototype,通过这个函数创建的对象,也会有一个隐式原型__proro__,会指向函数的原型对象,因此可以得出一下关系图。(红线代表 new 的关系)

proto01

然后,我们再思考,函数的原型对象是哪里来的?

当我们打印Foo.prototype时,会得到一个空对象

js
function Foo(name) {
  this.name = name;
}

const obj = new Foo("abc");

console.log(Foo.prototype) // {}

因此可以得出,函数的原型对象也是通过new Object()产生的。并且Object是一个构造函数,所以Object也会有一个原型对象,那么Foo的原型对象的隐式原型也就指向了Object的原型对象,如图:

proto02

TIP

Objdec的原型对象哪里来的?

这里会有一些特殊,由于Object.prototype也是一个对象,如果它也来自Object,那就会无限的循环下去了。所以Ojdect的原型对象是由 JS 引擎在处理的时候,直接安放在内存里面的,那么Object.prototype的隐式原型指向的就是null

已知Object是一个构造函数,那函数是如何创建的呢?

在 JS 中,函数都是通过new Function来创建的

js
const foo = new Function("a", "b", "return a + b");

console.log(foo(1, 2)) // 3

所以Object也是通过new Function来创建的,Function也是一个函数,那么也会有一个原型对象,并且Object的隐式原型会指向Funciton的原型对象。

js
console.log(Object.__proto__ === Function.prototype) // true

proto03

Function的原型是一个对象,那么又可以得出一下关系:

proto04

此时再回顾最开始的Foo函数,Foo也是通过new Function创建出来的,由此得出原型链的最终关系图:

proto05

最后还有一个特殊点就是Function,它也是有 JS 引擎直接放在内存里面。由Function作为起始,衍生出整个原型链关系图。