回答

  1. 原型链的设计目的,是为了实现对象的复制,原理是一个对象可以使用其原型链上所有的变量和方法。
  2. 对象被大体分为三类,原型对象,实例对象,构造函数。三者通过prototype、proto、constructor、new相互关联。
  3. 原型对象通过 __proto__ 构成的链,称为原型链。构造函数也可以通过 __proto__ 构成链。

    分析

    原型、构造函数、实例的关系

  4. 原型是一种特殊的对象,包含一些特殊的key。

  5. 构造函数是一种特殊的函数,可以执行并返回一个实例对象。
  6. 实例是一个普通对象。

【此处等待补充一张图】

原型链、构造函数链

  1. 原型的上面是原型,组成了一条原型链。
  2. 构造函数的上面是构造函数,组成了一条构造函数链。
  3. 在终点处有特殊机制(设计缺陷)防止造成循环,这个特殊机制就是 Function.proto===Function.prototype,这个机制保证了终点是 null。

举个例子:

  1. // @ts-nocheck
  2. class A {}
  3. class B extends A {}
  4. class C extends B {}
  5. console.log(
  6. typeof A, // function
  7. typeof A.prototype, // object
  8. C.__proto__ === B, // true,函数的上面是函数
  9. B.__proto__ === A, // true
  10. A.__proto__ === Function, // false,终点特殊处理
  11. A.__proto__ === Function.prototype, // 终点特殊处理,不再指向Function,而指向Function的原型
  12. // 但 typeof Function.prototype === "function"
  13. C.prototype.__proto__ === B.prototype, // true,原型的上面是原型
  14. B.prototype.__proto__ === A.prototype, // true
  15. A.prototype.__proto__ === Object.prototype, // true
  16. Object.prototype.__proto__ === null // 终点为null
  17. );

原型链是原型的链,顶层为 null

这句话虽然有一些直白,但却可以直接揭示其本质。
我们从一组代码看起,大家可以在浏览器中跑一跑,每行会输出什么?

Object.prototype.__proto__; // null
Function.prototype.__proto__.__proto__; // null
String.prototype.__proto__.__proto__; // null

class A {}
class B extends A {}
class C extends B {}
A.prototype.__proto__.__proto__; // null
B.prototype.__proto__.__proto__.__proto__; // null
C.prototype.__proto__.__proto__.__proto__.__proto__; // null

以上所有的输出均为 null,我们可以用图例表示:
【此处等待补充一张图】
以最后一条为例,文字说明

  1. C.prototype 是「C原型」
  2. C.prototype.__proto__ 是「B原型」
  3. C.prototype.__proto__.__proto__ 是「A原型」
  4. C.prototype.__proto__.__proto__.__proto__ 是「Object原型」
  5. C.prototype.__proto__.__proto__.__proto__.__proto__ 是「null」

以上就构成了一条原型链。

instanceof,isPrototypeOf

inatanceof 针对 function,其右边必须是一个 fucntion;isPrototypeOf 针对 object,其调用者必须是一个 object。

一些特殊的思考题

参考资料