发布于 2024 年 2 月 26 日,星期一
JavaScript代理模式通过创建一个代理对象来控制对原始对象的访问,从而在不改变原始对象的情况下实现功能增强。这种模式允许开发者在不直接修改原始对象的前提下,添加额外的逻辑或行为,如数据验证、缓存、日志记录等。代理模式的核心思想是解耦,使得代码更加灵活和可维护,同时避免了直接操作原始对象带来的风险。通过代理,开发者可以在不改变现有代码的基础上,逐步引入新功能,实现“偷懒”的效果,提升开发效率和代码质量。
系列首发于公众号『非同质前端札记』https://mp.weixin.qq.com/s?__biz=MzkyOTI2MzE0MQ==&mid=2247485576&idx=1&sn=5ddfe93f427f05f5d126dead859d0dc8&chksm=c20d73c2f57afad4bbea380dfa1bcc15367a4cc06bf5dd0603100e8bd7bb317009fa65442cdb&token=1071012447&lang=zh_CN#rd ,若不想错过更多精彩内容,请“星标”一下,敬请关注公众号最新消息。
var createImg = (function () {
var imgNode = document.createElement("img");
document.appendChild(imgNode);
return {
setSrc: function (src) {
imgNode.src = src;
},
};
})();
var proxyImg = (function () {
var img = new Image();
img.onload = function () {
createImg.setSrc(this.src);
};
return {
setSrc: function (src) {
createImg.setSrc("loading.gif");
img.src = src;
},
};
})();
proxyImg.setSrc("bg.jpg");
var createImg = (function () {
var imgNode = document.createElement("img");
document.appendChild(imgNode);
var img = new Image();
img.onload = function () {
imgNode.src = img.src;
};
return {
setSrc: function (src) {
imgNode.src = "loading.gif";
img.src = src;
},
};
})();
createImg.setSrc("bg.jpg");
对于一个类而言,应该仅有一个引起它改变的入口
。如果一个对象承担了很多职责,那这个对象将变得很臃肿,那引起它变化的原因可能会有很多个。如果一个对象承担的职责过多,就等于把这些职责耦合在了一起,这种耦合会导致代码很脆弱和低内聚的设计。// 真实对象类 - 用于存储数据
class Storage {
constructor() {
this.data = [];
}
storeData(data) {
// 模拟存储数据的操作
console.log(`Storing data: ${data}`);
this.data.push(data);
}
displayData() {
console.log("Stored data:", this.data);
}
}
// 代理对象类 - 延迟存储操作
class ProxyStorage {
constructor() {
this.storage = new Storage();
this.pendingData = [];
this.timer = null;
this.delay = 5000; // 定时存储的延迟时间设定为5秒
}
storeData(data) {
this.pendingData.push(data);
this.scheduleStorage();
}
scheduleStorage() {
if (!this.timer) {
this.timer = setTimeout(() => {
this.flushPendingData();
this.timer = null;
}, this.delay);
}
}
flushPendingData() {
this.pendingData.forEach((data) => {
this.storage.storeData(data);
});
this.pendingData = [];
}
displayData() {
this.storage.displayData();
}
}
// 使用代理对象进行数据存储
const proxyStorage = new ProxyStorage();
// 模拟数据产生
function generateData() {
const data = Math.random(); // 这里使用随机数作为数据示例
// 将数据添加到带存储的数据队列中,并启动定时器来延迟存储操作。当在延迟时间内再次调用 storeData 时,则会每次更新带存储的数据队列数据。当定时器触发时,代理对象则会调用 Storage 对象的存储方法,将所有带存储的数据存储起来。
proxyStorage.storeData(data);
}
// 调用 generateData() 来模拟产生数据
// 在某一个时间段内连续产生数据,但实际触发存储的时间是延迟了的
setInterval(generateData, 1000);
// 模拟数据存储结束后,手动调用 displayData() 显示已存储的数据
setTimeout(() => {
proxyStorage.displayData();
}, 15000); // 这里设定15秒后结束数据存储并展示存储结果
// 真实对象类 - 用于存储数据
class Storage {
constructor() {
this.data = [];
}
storeData(data) {
// 模拟存储数据的操作
console.log(`Storing data: ${data}`);
this.data.push(data);
}
displayData() {
console.log("Stored data:", this.data);
}
}
// 代理对象类 - 延迟存储操作和定时暂停
class ProxyStorage {
constructor() {
this.storage = new Storage();
this.pendingData = [];
this.timer = null;
this.delay = 5000; // 定时存储的延迟时间设定为5秒
this.paused = false; // 初始状态为未暂停
}
storeData(data) {
this.pendingData.push(data);
this.scheduleStorage();
}
scheduleStorage() {
if (!this.paused && !this.timer) {
this.timer = setTimeout(() => {
this.flushPendingData();
this.timer = null;
}, this.delay);
}
}
flushPendingData() {
this.pendingData.forEach((data) => {
this.storage.storeData(data);
});
this.pendingData = [];
}
pause() {
this.paused = true;
clearTimeout(this.timer);
this.timer = null;
}
restart() {
this.paused = false;
this.scheduleStorage();
}
stop() {
this.pause();
this.pendingData = [];
}
displayData() {
this.storage.displayData();
}
}
// 使用代理对象进行数据存储
const proxyStorage = new ProxyStorage();
// 模拟数据产生
function generateData() {
const data = Math.random(); // 这里使用随机数作为数据示例
proxyStorage.storeData(data);
}
// 调用 generateData() 来模拟产生数据
// 在某一个时间段内连续产生数据,但实际触发存储的时间是延迟了的
const intervalId = setInterval(generateData, 1000);
// 模拟数据存储进行一段时间后,停止定时器并清空待存储的数据
setTimeout(() => {
// proxyStorage.stop();
proxyStorage.pause();
console.log("Timer stopped and pending data cleared");
}, 8000); // 这里设定8秒后停止定时器和清空待存储的数据
// 模拟数据存储结束后,手动调用 displayData() 显示已存储的数据
setTimeout(() => {
proxyStorage.displayData();
}, 15000); // 这里设定15秒后结束数据存储并展示存储结果
// 模拟数据存储恢复定时器
setTimeout(() => {
proxyStorage.restart();
console.log("Timer restarted");
clearInterval(intervalId);
}, 20000); // 这里设定20秒后恢复定时器
// input:
const movieServiceProxy = new CachedMovieServiceProxy();
console.log(movieServiceProxy.getMovie(1)); // 输出电影信息并缓存
console.log(movieServiceProxy.getMovie(2)); // 输出电影信息并缓存
console.log(movieServiceProxy.getMovie(1)); // 从缓存中输出电影信息
// output:
// Fetching movie with id 1 from the database...
// Caching movie with id 1...
{ id: 1, title: "Movie A", director: "Director A" }
// Fetching movie with id 2 from the database...
// Caching movie with id 2...
{ id: 2, title: "Movie B", director: "Director B" }
// Retrieving movie with id 1 from cache...
{ id: 1, title: "Movie A", director: "Director A" }
// 真实对象类 - 电影服务
class MovieService {
constructor() {
// 模拟电影数据
this.movies = [
{ id: 1, title: "Movie A", director: "Director A" },
{ id: 2, title: "Movie B", director: "Director B" },
{ id: 3, title: "Movie C", director: "Director C" },
];
}
// 获取电影信息
getMovie(id) {
console.log(`Fetching movie with id ${id} from the database...`);
// 模拟从数据库获取电影信息的操作
const movie = this.movies.find((movie) => movie.id === id);
return movie;
}
}
// 代理对象类 - 缓存代理
class CachedMovieServiceProxy {
constructor() {
this.movieService = new MovieService();
this.cache = {};
}
// 获取电影信息(代理方法)
getMovie(id) {
if (this.cache[id]) {
// 如果缓存中有对应的电影信息,则直接返回缓存数据
console.log(`Retrieving movie with id ${id} from cache...`);
return this.cache[id];
} else {
// 否则,调用真实对象的方法获取电影信息,并将结果存入缓存
const movie = this.movieService.getMovie(id);
console.log(`Caching movie with id ${id}...`);
this.cache[id] = movie;
return movie;
}
}
}
// 使用代理对象获取电影信息
const movieServiceProxy = new CachedMovieServiceProxy();
console.log(movieServiceProxy.getMovie(1)); // 第一次请求,从真实对象获取并缓存
console.log(movieServiceProxy.getMovie(2)); // 第二次请求,从真实对象获取并缓存
console.log(movieServiceProxy.getMovie(1)); // 第三次请求,从缓存中获取
曾探
大佬的《JavaScript 设计模式与开发实践》。文章仅做个人学习总结和知识汇总Q:(question)
R:(result)
A:(attention matters)
D:(detail info)
S:(summary)
Ana:(analysis)
T:(tips)