深入浅出 JS 原型链:.prototype、proto、constructor 全解析

37 阅读1分钟

在 JavaScript 的世界里,对象的能力不是与生俱来的,
而是沿着一条看不见的链,一路“借”来的。
这条链,叫原型链;它的起点是 .prototype,终点是 null


一、共享的力量:.prototype 的使命

假设我们想造很多辆车:

function Car(brand) {
  this.brand = brand;
}

如果每辆车都要自己带一套“启动”“刹车”的方法,那内存开销太大了。于是 JavaScript 提供了一个共享空间:构造函数的 .prototype 属性

Car.prototype.start = function() {
  console.log(`${this.brand} 启动了!`);
};

现在,所有通过 new Car() 创建的实例,都能共享这个 start 方法:

const su7 = new Car("小米SU7");
const model3 = new Car("特斯拉");

console.log(su7.start === model3.start); // true ✅ 同一个函数

这就是 .prototype 的核心价值:一次定义,处处复用


二、两种写法:精细添加 vs 整体重构

给原型添加方法,常见两种方式:

方式1:逐个挂载(推荐)

Car.prototype.start = function() { ... };
Car.prototype.stop = function() { ... };
  • 不破坏原有结构
  • 自动保留 constructor 属性

方式2:整体赋值

Car.prototype = {
  start() { ... },
  stop() { ... }
};
  • 看起来更整洁,但会丢失默认的 constructor
  • 因为 {} 是一个全新对象,没有指向 Carconstructor

✅ 修复方案:

Car.prototype = {
  constructor: Car, // 手动补回
  start() { ... },
  stop() { ... }
};

小建议:日常开发优先用“逐个添加”,批量重构时记得补 constructor


三、实例的“隐形纽带”:.__proto__

当你执行 const su7 = new Car("小米SU7"),JavaScript 会悄悄做一件事:

su7__proto__ 指向 Car.prototype

验证一下:

console.log(su7.__proto__ === Car.prototype); // true

这意味着:

  • su7 自己没有 start
  • 没关系,JS 会自动去 su7.__proto__(即 Car.prototype)里找。

这条“隐形纽带”,就是实例访问共享方法的桥梁。

⚠️ 注意:__proto__ 是非标准属性,仅用于调试或理解。生产环境请用 Object.getPrototypeOf(su7)


四、标准 API 与回溯之锚:getPrototypeOfconstructor

更规范的方式:Object.getPrototypeOf()

Object.getPrototypeOf(su7) === Car.prototype; // true

这是 ECMAScript 标准方法,比 __proto__ 更可靠。

回溯源头:constructor

每个原型对象默认有一个 constructor 属性,指回它的构造函数:

console.log(Car.prototype.constructor === Car); // true
console.log(su7.constructor === Car);          // true(通过原型链继承)

用途举例:

  • 动态创建同类型对象:new obj.constructor("新品牌")
  • 类型判断(需谨慎,因可被篡改)

五、为什么连 .toString() 都能用?

即使你没写任何方法,也能调用:

console.log(su7.toString()); // "[object Object]"

秘密在于:所有对象的原型链最终都通向 Object.prototype

查找路径如下:

su7 
  → su7.__proto__ = Car.prototypeCar.prototype.__proto__ = Object.prototype 
      → 找到 Object.prototype.toString()

所以,toStringhasOwnPropertyvalueOf 等方法,其实是“祖传”的。

你也可以覆盖它,让输出更友好:

Car.prototype.toString = function() {
  return `🚗 ${this.brand}`;
};
console.log(String(su7)); // "🚗 小米SU7"

六、终点:null 为一切画上句号

原型链不会无限延伸。当我们继续往上走:

console.log(Object.prototype.__proto__); // null

null 表示“此处再无原型”。它是整个原型系统的终止符

  • 当 JS 查找属性一路走到 null 仍未找到,就返回 undefined
  • 这保证了查找过程必定终止,不会死循环

正如万物生于有,有生于无;
JavaScript 的对象能力,始于委托,终于 null


七、 图形快速理解总结

66b94b61f939741c0ca1db2e69984697.png

这张图可以帮助我们理解js原型的原理

✅ 结语:原型不是魔法,而是设计

.prototype 提供共享,__proto__ 建立链接,Object.prototype 提供基础能力,null 守住边界——
这四者共同构成了 JavaScript 灵活而强大的原型系统。

理解它,你不仅能写出更高效的代码,还能看懂 Vue、React 等框架底层的继承逻辑。

下次有人问:“JS 有类吗?”
你可以笑着说: “我们有原型,比类更自由。”