解密JavaScript原型:从入门到精通
引言:为什么需要原型?
想象一下,你要为一个汽车工厂设计蓝图。每辆汽车都有一些共同特征(如车轮、发动机),但也有一些独特特征(如颜色、车牌号)。如果每辆车都要重新“学习”这些共同特征,那将是多么低效!
JavaScript中的原型机制,就像是这个汽车工厂的“共享知识库”,让所有汽车都能继承共同特征,同时保留自己的独特属性。
一、显示原型(prototype)
1.1 什么是原型?
// 每个函数都有一个天生的“宝箱”——prototype
function Car() {
// 构造函数
}
console.log(Car.prototype); // 这个“宝箱”默认是空的,但确实存在!
prototype就像是汽车设计图的“共享配件库”,里面存放着所有同款汽车都能使用的通用配件。
1.2 原型的实际应用
// 低效方式:每辆车都要“重新学会”这些特征
function InefficientCar(color) {
this.color = color;
this.wheels = 4; // 每次创建都要重复声明
this.engine = 'V6'; // 每次创建都要重复声明
}
// 高效方式:把共同特征放到原型“配件库”中
function EfficientCar(color) {
this.color = color; // 只声明独特属性
}
// 把通用特征放到原型上
EfficientCar.prototype.wheels = 4;
EfficientCar.prototype.engine = 'V6';
EfficientCar.prototype.run = function() {
return '汽车启动了!';
};
核心优势:
- 性能提升:通用方法只需定义一次,所有实例共享
- 内存节省:方法不重复创建,节约内存空间
- 维护方便:修改一次,所有实例同步更新
二、隐式原型(__ proto __)
2.1 对象的秘密通道
function Car(color) {
this.color = color;
}
Car.prototype.brand = '小米汽车';
const myCar = new Car('红色');
// 实例对象如何访问原型上的属性?
console.log(myCar.brand); // '小米汽车'
// 答案:通过__proto__这个“秘密通道”!
console.log(myCar.__proto__ === Car.prototype); // true
prototype是设计图的“共享配件库”
__proto__是每辆汽车的“配件领取通道”
当汽车需要某个配件(属性/方法)时,先看自己有没有,没有就去“通道”另一端的“配件库”找
2.2 查找机制演示
function Person(name) {
this.name = name; // 显示属性
}
Person.prototype.sayHello = function() { // 原型属性
console.log(`你好,我是${this.name}`);
};
const person = new Person('小明');
console.log(person.name); // 1. 先在自身查找 → 找到“小明”
console.log(person.age); // 2. 自身没有age → 去__proto__找 → 没找到
console.log(person.sayHello); // 3. 自身没有 → 去__proto__找 → 找到函数
三、new操作符的魔法
3.1 new到底做了什么?
让我们一步步揭开new的神秘面纱:
function Car(color) {
this.color = color;
}
// 当执行 new Car('蓝色') 时,JavaScript默默做了这些事:
function模拟New操作(constructor, ...args) {
// 1️⃣ 创建一个纯净的新对象
const obj = {};
// 2️⃣ 建立原型链接(关键步骤!)
obj.__proto__ = constructor.prototype;
// 3️⃣ 绑定this并执行构造函数
constructor.apply(obj, args);
// 4️⃣ 返回创建的对象
return obj;
}
const myCar = 模拟New操作(Car, '蓝色');
3.2 实际案例演示
function Phone(model) {
// new操作符暗中做了:this = {}
this.model = model;
this.turnOn = function() {
console.log(`${this.model}开机了!`);
};
// new操作符暗中做了:return this
}
Phone.prototype.brand = '小米'; // 所有小米手机共享的品牌
const myPhone = new Phone('14 Pro');
console.log(myPhone.model); // '14 Pro' ← 自身属性
console.log(myPhone.brand); // '小米' ← 通过原型找到
console.log(myPhone.__proto__ === Phone.prototype); // true
四、原型链 - JavaScript的继承之道
4.1 什么是原型链?
// 三层继承关系:曾祖父 → 爷爷 → 爸爸 → 儿子
function GreatGrandfather() {}
GreatGrandfather.prototype.familyName = '张家';
function Grandfather() {}
// 建立继承关系:爷爷继承曾祖父
Grandfather.prototype = new GreatGrandfather();
Grandfather.prototype.house = '四合院';
function Father() {}
// 爸爸继承爷爷
Father.prototype = new Grandfather();
Father.prototype.car = '奔驰';
function Son() {}
// 儿子继承爸爸
Son.prototype = new Father();
Son.prototype.gameConsole = 'PS5';
const littleZhang = new Son();
// 原型链查找演示:
console.log(littleZhang.gameConsole); // 1. 自身 → PS5 ✓
console.log(littleZhang.car); // 2. 自身无 → 爸爸 → 奔驰 ✓
console.log(littleZhang.house); // 3. 自身无 → 爸爸无 → 爷爷 → 四合院 ✓
console.log(littleZhang.familyName); // 4. ... → 曾祖父 → 张家 ✓
4.2 原型链的终点
所有原型链的尽头都是null:
const obj = {};
// 沿着原型链向上追溯:
console.log(obj.__proto__); // Object.prototype
console.log(obj.__proto__.__proto__); // null
// 验证原型链终点
console.log(Object.prototype.__proto__); // null
最后
原型不是魔法,而是一种优雅的共享机制。就像现实世界中的图书馆,我们不需要每人买一本同样的书,而是共享图书馆的资源。
学习原型就像学习骑自行车,一开始可能会摔倒几次,但一旦掌握,你就能在JavaScript的世界里自由驰骋!加油,未来的JavaScript高手!