JavaScript原型与原型链:从小米SU7到孔子哲学

56 阅读3分钟

JavaScript原型与原型链:从小米SU7到孔子哲学

从"如何拿到小米SU7"说起

想象一下,我们要在JavaScript中创建一辆小米SU7汽车:

// 类/构造函数 Car
function Car(model, color) {
    this.model = model;  // 车型
    this.color = color;  // 颜色
    this.speed = 0;     // 每个实例自己的速度属性
}

// 通过 new Car 实例化
let myCar = new Car("小米SU7", "星际蓝");

构造函数的双重身份

constructor 构造函数

  • 当使用 new Car()时,运行这个构造函数
  • this指向新创建的对象,构造新对象的过程
  • 设置的属性(如 model, color, speed)是每个实例自己独有的

prototype 原型对象

  • 设置的属性和方法,让这个类的所有实例共享
  • 比如给所有汽车添加"启动"方法:
Car.prototype.start = function() {
    console.log(this.model + " 启动了!");
};

Car.prototype.accelerate = function(speed) {
    this.speed += speed;
    console.log(this.model + " 加速到 " + this.speed + "km/h");
};

// 所有Car实例都能使用这些方法
myCar.start();        // 小米SU7 启动了!
myCar.accelerate(60); // 小米SU7 加速到 60km/h

孔子是韩国人?JS的原型式哲学

JavaScript的面向对象是原型式的面向对象,这更像是一种哲学思想:

  • 传统class面向对象:血缘关系,class是定义属性和方法的模板(抽象),实例是具体的
  • JS原型式面向对象:不是血缘关系,而是通过prototype实现的"共享经济"

虽然方法不是直接设置在实例上,但实例可以通过原型链访问到这些共享的方法。

深入理解prototype机制

什么是prototype?

  • JS为了完成原型式的面向对象构建:

    • 实例的独有属性用constructor设置
    • 实例的共享方法用prototype设置
  • 每个函数都有一个prototype属性,指向一个对象

  • 这个对象就是原型对象

prototype的核心特性

function Car() {}

// prototype属性的值是一个对象
console.log(typeof Car.prototype); // "object"

// 这个对象上的属性和方法会被所有实例共享
Car.prototype.wheels = 4;  // 所有汽车都有4个轮子

let car1 = new Car();
let car2 = new Car();
console.log(car1.wheels); // 4
console.log(car2.wheels); // 4

原型链的完整机制

__proto__与 constructor的三角关系

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

let myCar = new Car("小米SU7");

// 1. 实例的 __proto__ 指向构造函数的 prototype
console.log(myCar.__proto__ === Car.prototype); // true

// 2. 原型对象的 constructor 指向构造函数
console.log(Car.prototype.constructor === Car); // true

// 3. 构造函数的 prototype 指向原型对象
console.log(Car.prototype === Car.prototype); // true

原型链的层级关系

// 任何对象都有默认指向 Object.prototype
console.log(myCar.__proto__ === Car.prototype); // true
console.log(Car.prototype.__proto__ === Object.prototype); // true

// Object.prototype 的原型是 null
console.log(Object.prototype.__proto__ === null); // true

完整的原型链查找过程

当我们访问一个对象的属性或方法时:

// 查找 myCar.toString() 的过程:
// 1. 先看 myCar 自己有没有 toString?没有
// 2. 看 myCar.__proto__ (Car.prototype) 有没有?没有
// 3. 看 Car.prototype.__proto__ (Object.prototype) 有没有?有!
myCar.toString(); // 输出: [object Object]

实际应用示例

创建小米SU7的完整示例

// 构造函数 - 设置实例特有属性
function XiaomiSU7(version, battery) {
    this.brand = "小米";
    this.model = "SU7";
    this.version = version;    // 版本:标准版/Pro版/Max版
    this.battery = battery;    // 电池容量
    this.range = 0;           // 实际续航
}

// 原型方法 - 所有小米SU7共享的功能
XiaomiSU7.prototype.calculateRange = function() {
    // 根据电池容量计算续航
    this.range = this.battery * 8; // 假设每度电跑8公里
    return this.range;
};

XiaomiSU7.prototype.charge = function() {
    console.log(`${this.brand} ${this.model} ${this.version} 正在充电`);
};

XiaomiSU7.prototype.drive = function(distance) {
    console.log(`驾驶${this.brand}${this.model}行驶了${distance}公里`);
};

// 创建实例
let su7Max = new XiaomiSU7("Max版", 100); // 100度电池
let su7Pro = new XiaomiSU7("Pro版", 80);  // 80度电池

// 使用共享方法
su7Max.calculateRange(); // 计算续航:800公里
su7Pro.calculateRange(); // 计算续航:640公里

su7Max.drive(100); // 驾驶小米SU7行驶了100公里

总结:原型链的核心思想

  1. 构造函数:创建实例,设置实例特有属性
  2. prototype原型:存放所有实例共享的方法和属性
  3. __proto__:实现方法和属性的继承查找
  4. Object.prototype:所有对象的最终原型(除了null)
  5. null:原型链的终点,停止查找

这种设计让JavaScript实现了高效的代码复用:实例各自保持独立的数据,同时共享相同的行为方法。就像所有小米SU7都有相同的驾驶方法,但每辆车的颜色、配置可以各不相同。


通过这个从具体例子到抽象原理的讲解,相信你已经彻底理解了JavaScript的原型和原型链机制!