Skip to content

原型和原型链怎么回答? ​

函数有 prototype。new 出来的实例通过内部槽 [[Prototype]] 指向它。读属性时沿着这条链向上找,直到 null。class 是这套机制的语法糖。

原理 ​

js
function Person(name) {
  this.name = name
}
Person.prototype.say = function () {
  return this.name
}
const p = new Person("a")

new 做四件事:

  1. 创建一个空对象。
  2. 把这个对象的 [[Prototype]] 指到 Person.prototype。
  3. 用它作为 this 执行构造函数。
  4. 构造函数返回对象时用返回值,否则返回这个新对象。

p.say 在 p 自己身上没有,到 Person.prototype 找到。p.toString 还要再上到 Object.prototype。链的终点是 null:Object.getPrototypeOf(Object.prototype) === null。

实例上的数据各自一份,原型上的方法被实例共享。方法放原型,状态放实例。

class / extends 仍然是原型。extends 会让 Student.prototype 的原型指向 Person.prototype,并让 Student 的原型指向 Person,所以静态方法也能沿构造函数这条链找到。

js
class Student extends Person {
  constructor(name, grade) {
    super(name)
    this.grade = grade
  }
}

整对象替换原型时,原来的 constructor 指向会丢,需要自己补回去。

追问 ​

prototype 和 __proto__ 呢?

prototype 是函数上的属性,用来当实例的原型。__proto__ 是访问实例 [[Prototype]] 的历史写法,标准接口是 Object.getPrototypeOf / Object.setPrototypeOf。热路径上不要改已有对象的原型,引擎的隐藏类优化会失效。

instanceof 怎么实现?

从左边对象开始沿原型链走,看能不能遇到右边构造函数的 prototype。

为什么说 class 是语法糖?

class 规定了必须 new、原型方法不可枚举、派生类要先 super。运行时仍然是构造函数加原型链,没有另一套对象模型。

易错点 ​

  • 普通对象没有 prototype,除非自己加上。函数才有。
  • 原型链的尽头是 null,不是 Object.prototype。
  • 写在 this 上的属性会挡住原型上的同名属性。

相关 ​