跳到主要内容

原型与原型链是什么?属性查找规则怎么走?

面试速答(30 秒版 TL;DR)

  • 每个对象都有一个隐藏属性 [[Prototype]](可用 Object.getPrototypeOf(obj) 读取),指向另一个对象或 null
  • 访问属性时:先查对象自身属性,找不到就沿着 [[Prototype]] 一层层向上找,直到 null,这条链就是原型链
  • 函数(构造器)有 prototype 属性:new Foo() 创建实例时,会把实例的 [[Prototype]] 指向 Foo.prototype
  • 常见误区:obj.__proto__ 是历史非标准接口(现在多数实现仍支持),推荐用标准 API。

心智模型:两条线要分清

  • 实例.__proto__(标准名 [[Prototype]])指向“它继承谁”
  • 构造函数.prototype 指向“实例共享方法放哪”

属性查找(Property Lookup)规则

const obj = { a: 1 };
obj.a; // 先查 obj 自身
obj.toString; // 自身没有 -> 去 Object.prototype 找

查找顺序(面试口述版):

  1. 查对象自己的属性(含自有属性、访问器属性)。
  2. 沿 [[Prototype]] 去原型对象查。
  3. 一直查到 null,还没找到就是 undefined

补充:如果是 in 操作符,它会沿原型链判断是否存在:

"toString" in obj; // true(来自原型)
obj.hasOwnProperty("toString"); // false(不是自有属性)

new 发生了什么(高频)

面试常用的“4 步描述”:

  1. 创建一个空对象 o
  2. 设置 o.[[Prototype]] = Foo.prototype
  3. o 作为 this 执行构造函数 Foo
  4. 如果构造函数返回对象则用该对象,否则返回 o

典型追问

Q1:__proto__prototype 的区别?

  • __proto__(或 Object.getPrototypeOf):对象实例的原型指针。
  • prototype:函数对象的属性,给 new 出来的实例当原型用的“共享对象”。

Q2:原型链的终点是什么?

大多数普通对象的终点是 Object.prototype,再往上是 null

Object.getPrototypeOf(Object.prototype) === null; // true

易错点/坑

  • “继承就是复制”:不是。原型继承是“委托查找”,没找到才去原型找。
  • 改实例上的同名属性不会改原型:实例会创建/覆盖自己的属性,原型不变。
  • __proto__ 当标准:工程上用 Object.getPrototypeOf / Object.setPrototypeOf

速记要点(可背诵)

  • 对象通过 [[Prototype]] 形成原型链,属性查找沿链向上。
  • new Foo() 让实例的 [[Prototype]] 指向 Foo.prototype