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公里
总结:原型链的核心思想
- 构造函数:创建实例,设置实例特有属性
- prototype原型:存放所有实例共享的方法和属性
__proto__链:实现方法和属性的继承查找- Object.prototype:所有对象的最终原型(除了null)
- null:原型链的终点,停止查找
这种设计让JavaScript实现了高效的代码复用:实例各自保持独立的数据,同时共享相同的行为方法。就像所有小米SU7都有相同的驾驶方法,但每辆车的颜色、配置可以各不相同。
通过这个从具体例子到抽象原理的讲解,相信你已经彻底理解了JavaScript的原型和原型链机制!