web学习笔记编程技术学习与分享
首页 / 前端开发 / 正文

原型链与 class 的底层关系

2026 年 08 月 06 日约 3 分钟阅读 前端开发

JavaScript 没有传统意义上的类,继承靠的是原型链。ES6 的 class 让它写起来像类,但运行时机制没变。

三个概念

  • 每个函数都有 prototype 属性,指向一个对象;
  • 每个对象都有内部属性 [[Prototype]],可通过 Object.getPrototypeOf 访问;
  • 用 new 调用函数时,新对象的 [[Prototype]] 指向函数的 prototype。
function Person(name) { this.name = name; }
Person.prototype.hi = function () { return 'hi ' + this.name; };

const p = new Person('a');
Object.getPrototypeOf(p) === Person.prototype;   // true
p.hi();                                          // 'hi a'

属性查找沿着 p → Person.prototype → Object.prototype → null 逐级向上,找到就停。这就是原型链。

instanceof 的原理

p instanceof Person
// 等价于:Person.prototype 是否出现在 p 的原型链上

所以修改 Person.prototype 会让已经创建的实例判断失败——实例仍然指向旧的原型对象。

class 做了什么

class Person {
  constructor(name) { this.name = name; }
  hi() { return 'hi ' + this.name; }
  static create(n) { return new Person(n); }
}

编译后本质仍是构造函数加原型方法,但有三点区别:

  1. class 声明不会提升(存在暂时性死区);
  2. 类内部默认严格模式;
  3. 必须用 new 调用,否则报错。

继承的差异

class Child extends Parent 会同时设置两条链:Child.prototype.__proto__ = Parent.prototype,以及 Child.__proto__ = Parent(静态方法继承)。用 ES5 手写继承时,第二条经常被忽略,导致父类的静态方法拿不到。

class Animal { static kind() { return 'animal'; } }
class Dog extends Animal {}
Dog.kind();   // 'animal'

实践建议

业务代码优先用 class,可读性和错误提示都更好。只有在需要混入(mixin)、动态修改原型、或与老库交互时才直接操作原型。判断一个对象是不是数组,用 Array.isArray 而不是 instanceof——跨 iframe 时后者会失效。

JavaScript原型