深入理解 JavaScript 原型机制:从“如何拿到小米 SU7”说起

"孔子是韩国人?"------JS 的原型链不是血缘,而是委托。

在前端开发中,JavaScript 的原型(prototype)机制常常让人困惑又着迷。它不像 Java、C++ 那样基于"类"的继承,而是一种基于原型的委托式面向对象模型。今天,我们就用一个轻松的例子------"如何拿到一辆小米 SU7"------来揭开原型机制的神秘面纱。


一、构造函数:造车的第一步

假设你想拥有一辆小米 SU7,首先得有个"造车蓝图"。在 JS 中,这个蓝图就是构造函数:

ini 复制代码
Javascript
编辑
1function Car(color) {
2    this.color = color;
3}

当你执行 new Car('霞光紫') 时,JS 引擎会:

  1. 创建一个新对象;
  2. 将 this 指向这个新对象;
  3. 执行构造函数中的代码(如设置 color);
  4. 返回这个新对象。

此时,你得到了一辆"独属于你"的 SU7,颜色是霞光紫。

但问题来了:每辆车都要有 drive 方法、长宽高参数吗? 如果每个实例都存一份,岂不浪费内存?


二、prototype:共享的"公共配置"

于是 JS 引入了 prototype(原型) ------所有实例共享的方法和属性存放地。

javascript 复制代码
Javascript
编辑
1Car.prototype = {
2    name: 'su7',
3    height: 1.4,
4    weight: 1.5,
5    long: 4800,
6    drive() {
7        console.log('drive, 下赛道');
8    }
9};

现在,无论你创建多少辆 SU7,它们都共享同一份原型对象 。调用 car1.drive() 时,JS 会在 car1 自身找不到 drive,就沿着 __proto__ 链向上查找,最终在 Car.prototype 上找到。

✅ 关键点:

  • 实例属性(如 color)存在实例自身;
  • 共享方法/属性(如 drive, name)存在原型上;
  • 查找顺序:实例 → 原型 → 原型的原型 → ... → Object.prototype → null

三、原型链的本质:委托,而非继承

很多人误以为 JS 是"类继承",其实它是对象委托。

  • 传统 OOP(如 Java):子类"继承"父类,形成血缘关系。
  • JS 原型式 OOP:对象"委托"给另一个对象,说:"你有这方法吗?借我用用。"

比如:

ini 复制代码
Javascript
编辑
1var obj = {};
2console.log(obj.toString()); // 能调用!

obj 自己没有 toString,但它通过 __proto__ 委托给了 Object.prototype,于是成功调用。

这种设计更灵活:你可以随时修改原型,所有实例立即"感知"到变化(除非实例自己覆盖了该属性)。


四、constructor 与原型的"双向绑定"

每个函数都有 prototype 属性,而 prototype 对象默认有一个 constructor 属性,指回构造函数:

ini 复制代码
Javascript
编辑
1Car.prototype.constructor === Car; // true

但注意!如果你直接重写整个 prototype (如 Car.prototype = { ... }),这个链接会被切断:

ini 复制代码
Javascript
编辑
1Car.prototype = { drive() {} };
2console.log(Car.prototype.constructor === Car); // false! 现在是 Object

修复方式:

javascript 复制代码
Javascript
编辑
1Car.prototype = {
2    constructor: Car, // 手动修复
3    drive() { /*...*/ }
4};

或者使用 Object.defineProperty 设置不可枚举的 constructor。


五、多层原型链:Animal → Person → su

JS 支持原型链的嵌套,模拟"多级继承":

javascript 复制代码
Javascript
编辑
1function Animal() {}
2Animal.prototype.species = '动物';
3
4function Person() {}
5Person.prototype = new Animal(); // Person 委托给 Animal 实例
6
7var su = new Person();
8console.log(su.species); // '动物'

这里 su.__proto__ 是 Person.prototype(即一个 Animal 实例),而 Animal 实例的 __proto__ 又指向 Animal.prototype,最终通向 Object.prototype。

🌟 这就是为什么 su.toString() 依然有效------原型链一路向上,直到 Object.prototype。


六、思考:为什么 JS 选择原型?

  • 动态性:运行时可修改原型,行为实时生效;
  • 轻量:无需预定义"类",对象可自由扩展;
  • 哲学差异:JS 认为"对象之间可以互相学习",而非"必须出自某个模板"。

正如道家思想:"上善若水,水善利万物而不争。"

JS 的原型链,也是一种"无为而治"的委托之道。


结语

下次当你写下 new Car() 时,不妨想想:

你不仅创建了一个对象,还开启了一段沿着原型链向上委托的旅程 。

而那辆小米 SU7,或许不在展厅,而在 Car.prototype 的深处,静静等待被"驱动"。

相关推荐
子兮曰3 小时前
把 649 种文件格式塞进一个网页里:一个「文件不出浏览器」的预览器
前端·javascript·后端
paopaokaka_luck6 小时前
基于springboot+vue的智慧家政管理系统(AI问答、 WebSocket实时沟通、ECharts运营分析、订单规则与服务追踪)
java·javascript·spring boot·数据分析·echarts·mybatis
sm_926787056 小时前
RFID 标签打印的技术实现要点与二次开发实践
java·大数据·前端·c++·编辑器
IMPYLH6 小时前
HTML 的 <video> 元素
前端·html
溪语流沙7 小时前
【每天一个CSS | Day19】流光文字与一笔写成的手写签名
前端·javascript·css
逐光者9337 小时前
STM32——SPI 屏 · Flash · 高速
java·前端·stm32
To_OC7 小时前
同样是让 AI 写全栈,为什么别人一句话就够了
前端·前端框架·next.js
CDwenhuohuo8 小时前
electron pc项目打包成桌面端
前端·javascript·electron
前端大斗师8 小时前
「大于 1000」把 1000 元那单也算进去了:我在 Vue3 订单页对了 10 句话
前端·人工智能·typescript·大模型·原力计划
Jmyd01238 小时前
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路
前端·3d·vr·三维数字化