解密JavaScript原型:从入门到精通

167 阅读3分钟

解密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高手!