返回专题首页

JavaScript 专题

原型、原型链与 class:对象模型与继承机制的真实样子

JavaScript 的对象模型常常让人一开始觉得“不够直观”。你可能已经见过对象字面量、构造函数、`prototype`、`__proto__`、`class`、`extends` 这些写法,但如果没有形成一条完整主线,就很容易把它们看成互相独立的知识点。结果就是:会写 `cl

JavaScript 专题第 06 篇 / 25 篇6 分钟

JavaScript 的对象模型常常让人一开始觉得“不够直观”。你可能已经见过对象字面量、构造函数、prototype__proto__classextends 这些写法,但如果没有形成一条完整主线,就很容易把它们看成互相独立的知识点。结果就是:会写 class,却说不清它和原型链的关系;会给对象加方法,却不知道为什么有的方法会“从原型上继承”下来。

这一篇要解决的核心问题是:JavaScript 的对象不是靠传统类层级硬性组织起来的,而是建立在原型委托机制之上。class 只是更现代、更易读的语法外衣,底层心智并没有变。把这件事讲清楚,后面你理解框架实例、内建对象、数组方法和很多库的设计时都会轻松很多。

对象在 JavaScript 里到底是什么?

最朴素地说,对象就是一组键值对的集合。但在 JavaScript 里,对象不仅能存数据,还能通过原型链接到另一份对象能力。也就是说,一个对象除了自己拥有的属性外,还可能从它的原型对象那里“沿链路查找”属性和方法。

这和很多传统面向对象语言最大的不同在于,JavaScript 不是先靠类模板定义出固定结构,再实例化;它更像是对象与对象之间建立委托关系。你访问一个属性时,先找对象自己,如果没有,再沿原型链往上找,直到找到或到达链顶。

所以原型链的核心不是“继承语法”,而是“属性查找路径”。

prototype__proto__、构造函数到底是什么关系?

这是最容易把人绕晕的一组概念。可以先拆开看:

  • 函数对象通常有一个 prototype 属性,用来放未来实例共享的方法;
  • 某个对象内部会关联到它的原型对象,历史上很多人通过 __proto__ 观察这层关系;
  • 当你用构造函数创建实例时,实例通常会连接到构造函数的 prototype 对象。

真正重要的是理解“实例为什么能访问到构造函数原型上的方法”。不是因为它把方法复制了一份,而是因为查找属性时沿原型链找到了那份共享方法。这种机制让多个实例可以复用同一套行为,而不是每次创建对象都生成一份新方法。

为什么说 class 只是语法糖?

现代 JavaScript 为了让对象继承写起来更接近其他语言,引入了 classconstructorextendssuper 这些语法。它们确实让代码更整洁,但底层依然建立在原型和原型链之上。

这意味着,学 class 时不要只学表面写法,还要记住两件事:

  • 类方法依然挂在原型上,本质上还是共享行为;
  • 继承关系最终仍然表现为原型链委托,而不是另一套完全新的运行机制。

一旦把 class 当成“JavaScript 终于变成传统面向对象语言了”,后面遇到实例属性、静态方法、原型方法、继承覆盖和 this 指向问题时,就很容易理解错位。

原型链解决了什么问题?

它最核心解决的是行为共享。假设你要创建很多相似对象,比如一组用户对象、一组按钮对象、一组表单字段对象,如果每个对象都自己带一套完全相同的方法,会浪费内存,也不利于统一维护。原型链允许你把共享能力放在共同原型上,让实例只保存各自差异化的数据。

从工程角度看,这是一种非常重要的“共享与个性化”平衡方式。实例拥有自己的状态,共享方法由原型统一提供。后面你看数组为什么能直接用 mapfilterpush,本质上也是这些方法存在于对应原型链上。

继承是不是 JavaScript 中最重要的复用手段?

未必。很多初学者学到原型链后,会以为 JavaScript 的代码复用核心就是层层继承。实际上,现代前端和 Node 项目里,组合往往比继承更常见、更稳妥。原因很简单:继承层级一深,行为来源和修改影响范围都会变得更难追踪;而组合更容易控制边界和依赖。

所以,理解原型链非常重要,但不等于你要到处用继承。真正需要的是知道 JavaScript 对象模型如何工作,以及什么时候适合利用共享原型、什么时候更应该用组合、工厂函数或模块化来组织逻辑。

这一章在项目里最常见的坑

常见问题通常集中在下面几类:

  • 误把实例方法写进构造函数里,导致每个实例都创建一份;
  • 只会用 class,但说不清共享方法为什么不在实例自身上;
  • 修改原型对象时没有意识到会影响所有相关实例;
  • 把继承用成主要复用手段,导致层级过深;
  • 混淆“对象自有属性”和“沿原型链找到的属性”。

这些问题都提醒我们:对象模型不是为了炫技,而是为了更清楚地组织状态和共享行为。

和后面章节的关系

理解原型链之后,模块、内建对象、DOM 对象、Node API 对象乃至很多框架实例行为,都会更好解释。尤其是当你看到“这个方法为什么每个实例都能调用”“这个属性为什么查得到但自己明明没定义”时,原型链就是最基础的答案。

写在最后

JavaScript 的对象系统不靠传统类层级起家,而是靠原型委托工作。class 带来了更好的可读性,但并没有替换底层机制。学会把“共享行为”“实例状态”“属性查找路径”三件事放在一起理解,你对这门语言的掌控感会强很多。下一篇我们继续进入模块系统,看看 JavaScript 是怎样从单文件脚本走向工程化协作的。