结构型模式
- 享元模式:运用共享技术来减少创建对象的数量,从而减少内存占用提高性能
模式下将对象属性分为内部和外部
- 内部:可以被对象集合共享,通常不会改变
- 外部:根据应用场景而改变
利用时间来换取空间的优化模式,应用于需要大量创建重复类的代码块
//对象池
class ObjectPool{
constructor(){ this._pool=[]; }
//创建对象
create(obj){
return this._pool.length===0 ?
new obj(this) : this._pool.shift();
}
//对象回收
recover(obj){
return this._pool.push(obj)
}
//大小
size(){
return this._pool.length;
}
}
// 模拟文件对象
class File {
constructor(pool) {
this.pool = pool;
}
// 模拟下载操作
download() {
console.log(`+ 从 ${this.src} 开始下载 ${this.name}`);
setTimeout(() => {
console.log(`- ${this.name} 下载完毕`); // 下载完毕后, 将对象重新放入对象池
this.pool.recover(this);
}, 100);
}
}
let objPool = new ObjectPool();
let file1 = objPool.create(File);
file1.name = "文件1";
file1.src = "https://download1.com";
file1.download();
let file2 = objPool.create(File);
file2.name = "文件2";
file2.src = "https://download2.com";
file2.download();
setTimeout(() => {
let file3 = objPool.create(File);
file3.name = "文件3";
file3.src = "https://download3.com";
file3.download();
}, 200);
setTimeout(
() =>
console.log(
`${"*".repeat(50)}\n下载了3个文件,但其实只创建了${objPool.size()}个对象`
),
1000
);
- 代理模式:解决避免对一些对象的直接访问
// main.js
const myImg = {
setSrc(imgNode, src) {
imgNode.src = src;
}
};
// 利用代理模式实现图片懒加载
const proxyImg = {
setSrc(imgNode, src) {
myImg.setSrc(imgNode, "./image.png"); // NO1. 加载占位图片并且将图片放入<img>元素
let img = new Image();
img.onload = () => {
myImg.setSrc(imgNode, src); // NO3. 完成加载后, 更新 <img> 元素中的图片
};
img.src = src; // NO2. 加载真正需要的图片
}
};
let imgNode = document.createElement("img"),
imgSrc =
"https://upload-images.jianshu.io/upload_images/5486602-5cab95ba00b272bd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1000/format/webp";
document.body.appendChild(imgNode);
proxyImg.setSrc(imgNode, imgSrc);
- 桥接模式:抽象部分和具体实现部分分离,两者可独立变化,也可以一起工作
//此函数负责循环遍历数组每个元素,是抽象部分; 而回调函数callback就是具体实现部分。
const foreach=(arr,callback){
if(!Array.isArray(arr)){
return;
}
const length=arr.length;
for(let i=0;i<length;i++){
callback(arr[i],i)
}
}
let arr=[1,2]
foreach(arr,(el,index=> console.log(el,index)))
- 组合模式(树形模式)将对象组合成树形结构以表示“部分-整体”的层次结构。
// 文件类
class File {
constructor(name) {
this.name = name || "File";
}
add() {
throw new Error("文件夹下面不能添加文件");
}
scan() {
console.log("扫描文件: " + this.name);
}
}
// 文件夹类
class Folder {
constructor(name) {
this.name = name || "Folder";
this.files = [];
}
add(file) {
this.files.push(file);
}
scan() {
console.log("扫描文件夹: " + this.name);
for (let file of this.files) {
file.scan();
}
}
}
let home = new Folder("用户根目录");
let folder1 = new Folder("第一个文件夹"),
folder2 = new Folder("第二个文件夹");
let file1 = new File("1号文件"),
file2 = new File("2号文件"),
file3 = new File("3号文件");
// 将文件添加到对应文件夹中
folder1.add(file1);
folder2.add(file2);
folder2.add(file3);
// 将文件夹添加到更高级的目录文件夹中
home.add(folder1);
home.add(folder2);
// 扫描目录文件夹
home.scan();
- 装饰者模式:在不改变自身属性的基础上,动态添加功能代码
const addDecorator = (fn, before, after) => {
let isFn = fn => typeof fn === "function";
if (!isFn(fn)) {
return () => {};
}
return (...args) => {
let result;
// 按照顺序执行“装饰函数”
isFn(before) && before(...args);
// 保存返回函数结果
isFn(fn) && (result = fn(...args));
isFn(after) && after(...args);
// 最后返回结果
return result;
};
};
/******************以下是测试代码******************/
const beforeHello = (...args) => {
console.log(`Before Hello, args are ${args}`);
};
const hello = (name = "user") => {
console.log(`Hello, ${name}`);
return name;
};
const afterHello = (...args) => {
console.log(`After Hello, args are ${args}`);
};
const wrappedHello = addDecorator(hello, beforeHello, afterHello);
let result = wrappedHello("test.com");
console.log(result);
6.适配器模式:为多个不兼容接口之间提供“转化器”。
当数据不符合使用规则,就可以借助此种模式进行格式转化。
const API = {
qq: () => ({
n: "菊花台",
a: "周杰伦",
f: 1
}),
netease: () => ({
name: "菊花台",
author: "周杰伦",
f: false
})
};
const adapter = (info = {}) => ({
name: info.name || info.n,
author: info.author || info.a,
free: !!info.f
});
/*************测试函数***************/
console.log(adapter(API.qq()));
console.log(adapter(API.netease()));