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

54 阅读3分钟

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

在前端开发中,理解 JavaScript 的原型(prototype)机制是掌握其面向对象编程的关键。本文将以一个轻松的比喻——“如何拿到小米 SU7”——来深入浅出地讲解构造函数、实例、原型链等核心概念。


一、从“造车”开始:构造函数与实例

想象我们要“制造一辆小米 SU7”。在 JavaScript 中,这相当于定义一个构造函数

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

当我们执行 new Car("Xiaomi", "SU7") 时,JavaScript 会:

  1. 创建一个新对象;
  2. 将这个新对象的 __proto__ 指向 Car.prototype
  3. 执行构造函数,this 指向这个新对象;
  4. 返回这个新对象。

于是,我们就“拿到”了一辆属于自己的小米 SU7:

const su7 = new Car("Xiaomi", "SU7");
console.log(su7.brand); // "Xiaomi"

每个通过 new Car() 创建的实例(如 su7、su7Pro 等),都有自己独立的属性(如品牌、型号),这些是“私有”的,互不干扰。


二、共享能力:prototype 的作用

但现实中,所有小米 SU7 都应该具备一些共有的能力,比如“启动”、“加速”、“播放音乐”。如果把这些方法写在构造函数里,每次创建新车都会重复定义一次,浪费内存。

这时,JavaScript 提供了 prototype(原型) 机制:

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

Car.prototype.accelerate = function() {
  console.log("正在加速...");
};

现在,所有 Car 的实例都能调用这些方法,但它们实际上只在 Car.prototype 上定义了一次:

su7.start(); // "Xiaomi SU7 启动成功!"

这就是 原型式面向对象 的精髓:不是靠“血缘继承”,而是靠“共享原型”

📌 关键点:

  • 每个函数都有一个 prototype 属性,它是一个对象;
  • 所有通过该构造函数创建的实例,都能访问 prototype 上的属性和方法;
  • 这些方法在内存中只存在一份,高效且统一。

三、原型链:万物皆可追溯

当你调用 su7.start() 时,JavaScript 并不会在 su7 自身找到 start 方法。它会沿着一条“查找链”向上搜索:

  1. 先看 su7 自身有没有 start
  2. 没有?就去 su7.__proto__(即 Car.prototype)找;
  3. 还没有?继续去 Car.prototype.__proto__ 找;
  4. 最终到达 Object.prototype
  5. 如果 Object.prototype 也没有,就返回 undefined

这条链,就是著名的 原型链(Prototype Chain)

Object.prototype__proto__null,表示“到此为止,无路可走”:

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

这也解释了为什么所有对象最终都能使用 toString()hasOwnProperty() 等方法——它们都来自 Object.prototype


四、constructor 与原型的双向关系

值得注意的是,原型对象上还有一个特殊属性:constructor

console.log(Car.prototype.constructor === Car); // true

这意味着:原型知道是谁创造了它。同时,实例也可以通过原型反向找到构造函数:

console.log(su7.constructor === Car); // true

这种双向引用,使得 JavaScript 在运行时能灵活判断对象的“出身”。


五、JS 的哲学:原型式 vs 类式

传统面向对象语言(如 Java、C++)采用“类模板”方式:先定义类,再实例化。这是一种静态的、血缘式的继承

而 JavaScript 是动态的、委托式的:对象之间通过原型链“借用”能力,没有严格的父子血缘,只有“我能从谁那里拿到方法”。

正如孔子不是韩国人,但韩国文化受儒家影响——JS 对象不“拥有”方法,但能“使用”原型上的方法。

ES6 虽然引入了 class 语法,但它只是对原型机制的语法糖,底层依然是 prototype

class Car {
  constructor(brand, model) {
    this.brand = brand;
    this.model = model;
  }
  start() {
    console.log(`${this.brand} ${this.model} 启动成功!`);
  }
}
// 等价于上面的构造函数 + prototype 写法

结语:你真的“拿到”小米 SU7 了吗?

在 JavaScript 中,“拿到一辆车”不仅是拥有它的属性,更是能通过原型链,调用整个生态赋予它的能力。理解 prototype__proto__ 和构造函数之间的关系,就掌握了 JS 面向对象的命脉。

下次当你写下 new Car() 时,请记住:你不仅创建了一个对象,还连接了一条通往 Object.prototype 的古老链条——那是 JavaScript 世界万物的起点与归宿。

🚗 你的小米 SU7 已就位,prototype 引擎已启动,出发吧!