深入理解 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 的深处,静静等待被"驱动"。

相关推荐
Zyx20072 小时前
深入 JavaScript 事件机制:从冒泡到事件委托的高效实践
javascript
乐观的用户2 小时前
搞懂虚拟列表实现原理与步骤
前端·vue.js
Heo2 小时前
Webpack高级之常用配置项
前端·javascript·面试
Mike_jia2 小时前
DBSyncer:开源数据同步中间件全景实战指南
前端
y***86692 小时前
JavaScript在Node.js中的Electron
javascript·electron·node.js
烛阴3 小时前
从`new`关键字开始:精通C#类与对象
前端·c#
天若有情6733 小时前
笑喷!乌鸦哥版demo函数掀桌怒怼主函数:难办?那就别办了!
java·前端·servlet
记得记得就1513 小时前
【Nginx 实战系列(一)—— Web 核心概念、HTTP/HTTPS协议 与 Nginx 安装】
前端·nginx·http
天蓝色的鱼鱼3 小时前
mescroll老用户亲测z-paging:这些功能让我果断切换!
前端·uni-app