本文是自己学习 JavaScript 设计模式的笔记,整理了 JavaScript 常见的五种设计模式,并总结了相应的案例代码。
1. 装饰者模式
装饰者模式是指,在不改变对象自身代码的前提下,新增功能的模式。
如下代码所示,创建3个飞机对象。plane
可以发射普通子弹;plane2
不仅可以发射普通子弹,还可以发射导弹;plane3
不仅可以发射普通子弹和导弹,还可以发射炮弹。
创建构造函数Plane2
时,不改变plane
的功能,只增加功能,将Plane
的实例挂载到自身属性上,并在fire
方法下,调用Plane
的方法,并增加一个功能:发射导弹。Plane3
同理。
// 飞机大战
// 普通子弹 导弹 炮弹
// 发射普通子弹的飞机
function Plane() {}
Plane.prototype.fire = function () {
console.log('普通子弹');
};
// 可发射导弹的飞机
function Plane2(plane) {
this.plane = plane;
}
Plane2.prototype.fire = function () {
this.plane.fire();
console.log('导弹');
};
// 可发射炮弹的飞机
function Plane3(plane) {
this.plane = plane;
}
Plane3.prototype.fire = function () {
this.plane.fire();
console.log('炮弹');
};
const plane = new Plane();
const plane2 = new Plane2(plane); // 在plane上增加发射导弹
const plane3 = new Plane3(plane2); // 在plane2上增加发射炮弹
plane.fire();
// 普通子弹
plane2.fire();
// 普通子弹
// 导弹
plane3.fire();
// 普通子弹
// 导弹
// 炮弹
2. 策略模式
策略模式主要解决的是
if-else
逻辑复杂而不可维护的情况。
如下代码所示,模拟一个公司发奖金的场景。base
代表基础金额,grade
代表等级。grade
有s
、a
、b
、c
、d
五种等级,分别发放的奖金为5
、4
、3
、2
、1
倍的基础金额。
定义一个策略对象strategy
,分别有对应5种等级的函数,返回相应的金额。在计算奖金函数getBonus
中,只需传入相应的base
和grade
即可。如果要更改逻辑,只需在strategy
对象中更改策略,无需更改计算奖金函数的代码逻辑。
const strategy = {
s(base) {
return base * 5;
},
a(base) {
return base * 4;
},
b(base) {
return base * 3;
},
c(base) {
return base * 2;
},
d(base) {
return base * 1;
},
};
const getBonus = (base, grade) => strategy[grade](base);
const res1 = getBonus(1000, 's'); // 5000
const res2 = getBonus(1500, 'a'); // 6000
3. 代理模式
代理模式主要用于转发请求。
如下代码,模拟发送邮件的过程,但是发送者不直接将邮件送给接收者,而是将邮件交给邮递员,邮递员将邮件交给接收者。
// 创建邮件对象
function Mail(sender, content) {
this.sender = sender; // 发送者
this.content = content; // 邮件内容
}
// 发布者
const sender = {
// target:实际交给谁
// receiver:最终的接收者
// mail:发送的邮件
send(target, receiver, mail) {
target.send(receiver, mail);
},
};
// 邮递员
const postMan = {
// receiver:最终的接收者
// mail:发送的邮件
send(receiver, mail) {
receiver.receive(mail);
},
};
// 接收者
const receiver = {
// mail:接收到的邮件
receive(mail) {
console.log(`Receive <${mail.content}> from ${mail.sender}`);
},
};
const mail = new Mail('Jack', 'Hello');
// 邮件交给postMan,最终送给receiver,邮件是mail
sender.send(postMan, receiver, mail);
// Receive <Hello> from Jack
4. 发布订阅模式
发布订阅模式,也叫观察者模式。它定义对象间的一种一对多的关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知。
实际上,一个普通的事件绑定,也是一种发布订阅模式。如下代码所示,发布者为用户,监听者为绑定的事件。
document.addEventListener('click', e => {
console.log(e);
});
如下代码所示,模拟明星发布消息,粉丝收到消息的场景。在这个场景中,明星star
发布消息后,每个粉丝都能收到消息,并作出不同的反应。
发布者为star
,监听者为每个粉丝。
class Star {
constructor() {
// 粉丝队列
this.followers = [];
}
follow(obj) {
// 将obj加入粉丝队列
this.followers.push(obj);
}
publish() {
// 发布消息,通知粉丝
this.followers.forEach(item => {
item.fn(item.name);
});
}
}
const star = new Star();
// 三个用户关注star
star.follow({
name: 'Jack',
fn(name) {
console.log(`${name}看到消息!`);
},
});
star.follow({
name: 'Jone',
fn(name) {
console.log(`${name}看到消息,并点赞!`);
},
});
star.follow({
name: 'Tom',
fn(name) {
console.log(`${name}看到消息,并评论!`);
},
});
// star发布消息
star.publish();
// Jack看到消息!
// Jone看到消息,并点赞!
// Tom看到消息,并评论!
5. 迭代器模式
迭代器模式描述了一个方案,即可以把有些结构称为可迭代对象,因为它们实现了正式的
Iterable
接口,而且可以通过迭代器Iterator
消费。简单来说,遍历可迭代对象,就是一种迭代器模式。
如下代码所示,编写了一个倒序遍历数组的函数reverseEach
,实现倒序遍历。
const nums = [1, 2, 3, 4, 5];
const reverseEach = (arr, callback) => {
if (!Array.isArray(arr)) throw Error(`请传入数组!`);
const len = arr.length;
for (let i = len - 1; i >= 0; i--) {
callback.call(arr[i], arr[i], i);
}
};
reverseEach(nums, (num, index) => {
console.log(num, index);
});
// 5 4
// 4 3
// 3 2
// 2 1
// 1 0