Skip to content

原型与原型链

难度等级:🟡 中级(2-4年)
分类:JavaScript
归档日期:2026-05-14


📚 理论知识

核心概念

每个 JavaScript 对象都有一个内部链接指向另一个对象,这个对象就是它的原型。原型本身也是一个对象,也有自己的原型,如此形成链条。

  • 每个函数都有 prototype 属性(显式原型)
  • 每个对象都有 __proto__ 属性(隐式原型),指向其构造函数的 prototype

三者关系

js
function Person(name) {
  this.name = name
}

const p = new Person('Alice')

p.__proto__ === Person.prototype         // true
Person.prototype.constructor === Person  // true

关系链:

p (实例)
 └── __proto__ ──► Person.prototype
                     ├── constructor ──► Person(函数)
                     └── __proto__ ──► Object.prototype
                                          └── __proto__ ──► null

原型链(Prototype Chain)

当访问一个对象的属性时,JS 引擎会:

  1. 先在对象本身查找
  2. 找不到,沿 __proto__ 向上查找原型对象
  3. 继续向上,直到 Object.prototype
  4. 还找不到,返回 undefined(到达 null 终止)
js
function Animal(name) {
  this.name = name
}
Animal.prototype.speak = function () {
  return `${this.name} makes a sound.`
}

function Dog(name) {
  Animal.call(this, name)
}
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog

Dog.prototype.bark = function () {
  return `${this.name} barks!`
}

const dog = new Dog('Rex')

dog.bark()    // Rex barks!         ← 在 Dog.prototype 找到
dog.speak()   // Rex makes a sound. ← 在 Animal.prototype 找到
dog.toString() //                   ← 在 Object.prototype 找到

new 操作符做了什么

js
function myNew(Constructor, ...args) {
  // 1. 创建空对象,__proto__ 指向构造函数的 prototype
  const obj = Object.create(Constructor.prototype)
  // 2. 执行构造函数,绑定 this
  const result = Constructor.apply(obj, args)
  // 3. 若构造函数返回对象则用它,否则返回 obj
  return result instanceof Object ? result : obj
}

常见误区

问题要点
Object.create(null)创建无原型的纯净对象
instanceof 原理沿原型链查找是否存在目标 prototype
继承方式对比原型链继承 / 借用构造函数 / 组合继承 / class extends
hasOwnProperty只检查自身属性,不走原型链

❌ 错误答法示范

错误示例 1:混淆 prototype__proto__

js
// ❌ 错误理解
const obj = {}
obj.prototype  // undefined,普通对象没有 prototype!

❌ 问题:prototype 是函数专有属性,普通对象没有。对象的隐式原型是 __proto__,不是 prototype

错误示例 2:直接赋值覆盖 prototype 导致 constructor 丢失

js
// ❌ 错误写法
Dog.prototype = Animal.prototype

// ✅ 正确写法
Dog.prototype = Object.create(Animal.prototype)
Dog.prototype.constructor = Dog

❌ 问题:直接赋值会让两个类共享同一个 prototype 对象,修改 Dog.prototype 会污染 Animal.prototype。


🎤 模拟面试作答

嗯,这道题我来说一下我的理解。

原型这个概念,本质上是 JavaScript 实现继承和属性共享的底层机制。每个对象在创建的时候,都会有一个内部指针指向另一个对象,这个对象就是它的原型。

具体来说有两个属性要区分清楚:一个是函数才有的 prototype,是显式原型;另一个是每个对象都有的 __proto__,是隐式原型,它指向构造函数的 prototype

原型链就是这些指向关系串起来的链条。当我们访问一个属性的时候,JS 引擎会先在对象自身找,找不到就沿着 __proto__ 往上找,一直找到 Object.prototype,还没有就返回 undefined,这条链就叫原型链。

另外 new 操作符背后其实做了三件事:创建一个新对象、把它的 __proto__ 指向构造函数的 prototype、执行构造函数绑定 this

在实际项目里,现在大家都用 class extends 语法,但底层还是原型链那套机制,理解原型链能帮助我们更好地排查继承相关的 bug。

……如果面试官感兴趣,我还可以聊聊 class 和 ES5 组合继承在实现上的差异,或者 instanceof 的底层判断逻辑。


❓ 面试官追问预测

追问 1instanceof 是怎么判断的?能手写一个吗?

应对思路:沿左边对象的 __proto__ 链向上查找,看是否能找到右边构造函数的 prototype,找到返回 true,到 null 还没找到返回 false。

js
function myInstanceof(left, right) {
  let proto = Object.getPrototypeOf(left)
  while (proto) {
    if (proto === right.prototype) return true
    proto = Object.getPrototypeOf(proto)
  }
  return false
}

追问 2class 继承和 ES5 原型继承有什么区别?

应对思路:class 是语法糖,但有几个关键差异:class 必须用 new 调用;class 内部默认严格模式;super() 必须在 this 之前调用;class 的方法不可枚举。

追问 3:如何实现没有原型链的纯净对象?有什么用?

应对思路:Object.create(null),创建出来的对象没有 __proto__,不会继承 toStringhasOwnProperty 等,适合用作纯粹的 Map/字典,避免 key 冲突,性能也稍好。

追问 4:修改 prototype 上的属性,已经创建的实例会受影响吗?

应对思路:会。实例的 __proto__ 指向的是同一个 prototype 对象的引用,动态修改 prototype 上的属性,所有实例都能感知到。但如果是直接替换整个 prototype 对象,已创建的实例不受影响,因为 __proto__ 还指向旧的 prototype。

个人面试题归档,持续更新中