原型模式
(Prototype Pattern)原型模式是一种创建型设计模式,通过复制现有对象来创建新对象,而不是通过实例化类来创建。这种模式允许你通过克隆原型对象来创建新对象,避免了重复的初始化代码。
在 JavaScript 中,原型模式是语言的核心特性之一,因为 JavaScript 本身就是基于原型的语言。
核心概念
原型对象
每个 JavaScript 对象都有一个原型对象(prototype),新对象可以从原型对象继承属性和方法。
原型链
当访问一个对象的属性时,如果对象本身没有这个属性,JavaScript 会沿着原型链向上查找,直到找到该属性或到达原型链的末端。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| function Animal(name) { this.name = name; }
Animal.prototype.eat = function() { console.log(`${this.name} is eating.`); };
function Dog(name, breed) { Animal.call(this, name); this.breed = breed; }
Dog.prototype = Object.create(Animal.prototype); Dog.prototype.constructor = Dog;
Dog.prototype.bark = function() { console.log(`${this.name} is barking!`); };
const dog = new Dog('旺财', '中华田园犬'); dog.eat(); dog.bark();
console.log(dog.hasOwnProperty('name')); console.log(dog.hasOwnProperty('eat'));
|
实现方式
1. Object.create() 方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| const carPrototype = { wheels: 4, engine: 'V8', drive() { console.log(`驾驶这辆 ${this.brand} ${this.model}`); }, getInfo() { console.log(`品牌: ${this.brand}, 型号: ${this.model}, 引擎: ${this.engine}`); } };
const car1 = Object.create(carPrototype); car1.brand = 'Tesla'; car1.model = 'Model S';
const car2 = Object.create(carPrototype); car2.brand = 'BMW'; car2.model = 'X5';
car1.drive(); car2.getInfo();
|
2. 构造函数与原型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| function Person(name, age) { this.name = name; this.age = age; }
Person.prototype.introduce = function() { console.log(`大家好,我是 ${this.name},今年 ${this.age} 岁`); };
Person.prototype.sayHello = function() { console.log(`你好,我是 ${this.name}`); };
const person1 = new Person('张三', 25); const person2 = new Person('李四', 30);
person1.introduce(); person2.sayHello();
console.log(person1.introduce === person2.introduce);
|
3. 克隆方法实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| class Prototype { constructor() { this.data = {}; }
clone() { return Object.create( Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this) ); }
deepClone() { const cloned = this.clone();
for (let key in cloned) { if (cloned.hasOwnProperty(key) && typeof cloned[key] === 'object') { cloned[key] = JSON.parse(JSON.stringify(cloned[key])); } }
return cloned; } }
class Product extends Prototype { constructor(name, price, features) { super(); this.name = name; this.price = price; this.features = features; }
display() { console.log(`产品: ${this.name}, 价格: ${this.price}元`); console.log('特性:', this.features); } }
const originalProduct = new Product('iPhone', 6999, { color: '白色', storage: '128GB', camera: '双摄' });
const clonedProduct = originalProduct.clone(); clonedProduct.name = 'iPhone Pro'; clonedProduct.price = 8999; clonedProduct.features.camera = '三摄';
originalProduct.display();
const deepClonedProduct = originalProduct.deepClone(); deepClonedProduct.features.color = '黑色';
originalProduct.display();
|
实际应用场景
1. 插件系统
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| function Editor() { this.content = ''; }
Editor.prototype.plugins = [];
Editor.prototype.use = function(plugin) { this.plugins.push(plugin); plugin.init(this); };
const spellChecker = { init(editor) { editor.checkSpelling = function() { console.log('检查拼写...'); }; } };
const autoSave = { init(editor) { editor.autoSave = function() { console.log('自动保存:', this.content); }; } };
const editor = new Editor(); editor.use(spellChecker); editor.use(autoSave);
editor.checkSpelling(); editor.autoSave();
|
2. 配置对象克隆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| const defaultConfig = { theme: 'light', language: 'zh-CN', fontSize: 14, notifications: { email: true, sms: false, push: true }, display: { showSidebar: true, showToolbar: true } };
function createUserConfig(customConfig) { const config = JSON.parse(JSON.stringify(defaultConfig));
return Object.assign(config, customConfig); }
const userConfig1 = createUserConfig({ theme: 'dark', fontSize: 16 });
const userConfig2 = createUserConfig({ language: 'en-US', notifications: { email: false } });
console.log(userConfig1);
console.log(userConfig2);
|
3. 游戏角色克隆
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| class GameCharacter { constructor(config) { this.type = config.type; this.health = config.health; this.attack = config.attack; this.defense = config.defense; this.skills = [...config.skills]; }
clone() { return new GameCharacter({ type: this.type, health: this.health, attack: this.attack, defense: this.defense, skills: this.skills }); }
display() { console.log(`类型: ${this.type}`); console.log(`生命值: ${this.health}`); console.log(`攻击力: ${this.attack}`); console.log(`防御力: ${this.defense}`); console.log(`技能: ${this.skills.join(', ')}`); } }
const warriorPrototype = new GameCharacter({ type: '战士', health: 100, attack: 80, defense: 60, skills: ['冲锋', '旋风斩', '嘲讽'] });
const magePrototype = new GameCharacter({ type: '法师', health: 60, attack: 100, defense: 30, skills: ['火球术', '冰霜新星', '传送'] });
const warrior1 = warriorPrototype.clone(); const warrior2 = warriorPrototype.clone(); const mage1 = magePrototype.clone();
warrior1.health = 90; warrior1.display();
|
注意事项
- 引用类型的共享问题,需在构造函数中初始化引用类型属性
- 构造函数的 constructor 属性,重写原型对象时,记得修复 constructor 指向。
- 虽然原型可以节省内存,但过长的原型链会影响属性查找性能。
与其他模式的区别
原型模式 vs 工厂模式
- 原型模式:通过克隆现有对象创建新对象
- 工厂模式:通过工厂方法创建新对象
原型模式 vs 单例模式
- 原型模式:可以创建多个相似的对象实例
- 单例模式:确保只有一个实例存在
总结
原型模式是 JavaScript 的基础,理解原型模式对于掌握 JavaScript 至关重要。它的核心优势包括:
- 内存效率:通过共享方法减少内存占用
- 灵活性:支持动态扩展和修改
- 继承机制:通过原型链实现对象继承