__proto__、prototype、constructor

首先,我们需要牢记两点:① __proto__ 和 constructor 属性是对象所独有的;② prototype 属性是函数所独有的。但是由于 JS 中函数也是一种对象,所以函数也拥有 __proto__ 和 constructor 属性。

1. __proto__ 属性

仅留下 __proto__ 属性,它是对象所独有的,可以看到一个对象的 __proto__ 属性指向另一个对象,即指向它的原型对象(也可以理解为父对象)。

它的作用是:当访问一个对象的属性时,如果该对象内部不存在这个属性,就会去它的 __proto__ 指向的对象(父对象)里找,如果还没找到,则继续往上找……直到原型链顶端 null,这就是原型链。

const obj = { a: 1 };
obj.__proto__ === Object.prototype; // true

2. prototype 属性

prototype 属性是函数所独有的,它是从一个函数指向一个对象。它的含义是函数的原型对象,也就是这个函数(其实所有函数都可以作为构造函数)所创建的实例的原型对象;由此可知 f1.__proto__ === Foo.prototype,它们两个完全一样。

prototype 的作用是:包含可以由特定类型的所有实例共享的属性和方法,让该函数所实例化的对象们都可以找到公用的属性和方法。任何函数在创建的时候,其实会默认同时创建该函数的 prototype 对象。

3. constructor 属性

constructor 属性也是对象才拥有的,它是从一个对象指向一个函数,含义是指向该对象的构造函数。每个对象都有构造函数(本身拥有或继承而来,继承而来的要结合 __proto__ 属性查看会更清楚,如下图所示)。

Function 这个对象比较特殊,它的构造函数就是它自己(因为 Function 既可以看成函数,也可以看成对象),所有函数和对象最终都是由 Function 构造函数得来,所以 constructor 属性的终点就是 Function 这个函数。

单从 constructor 这个属性来讲,只有 prototype 对象才有;每个函数在创建的时候,JS 会同时创建一个该函数对应的 prototype 对象,而函数创建的对象 .__proto__ === 该函数.prototype,该函数 .prototype.constructor === 该函数本身,所以通过函数创建的对象即使自己没有 constructor 属性,也能通过 __proto__ 找到对应的 constructor,任何对象最终都可以找到其构造函数(null 除外)。

4. 三者关系示例

function Foo(name) { this.name = name; }
const f1 = new Foo('Tom');

f1.__proto__ === Foo.prototype;            // true
Foo.prototype.constructor === Foo;         // true
f1.constructor === Foo;                    // true(沿原型链找到)
Foo.__proto__ === Function.prototype;      // true
Function.__proto__ === Function.prototype; // true(自指)

补充:生产代码里访问原型推荐用 Object.getPrototypeOf(obj) 与 Object.setPrototypeOf(obj, proto),__proto__ 是历史遗留的 getter/setter,且被废弃过,性能也差一些。

来源整理自:vue3js.cn 面试官系列