1.JS常用设计模式分类
1.创建型模式
定义:创建型模式关注对象的创建过程,系统可以更加灵活、可配置,并且在创建对象时不必关心具体的类或构造过程。
-
1.单例模式 ⭐
-
2.抽象工厂模式
-
3.原型模式
2.结构型模式
定义:结构型模式关注如何组合对象和类来形成较大的结构,而不仅仅是组合它们的简单方式。结构型模式可以帮助确保系统中的各个部分能够清晰、有序地一起工作,同时也能为将来的改变或扩展提供更好的灵活性。
-
1.适配器模式
-
2.装饰器模式 ⭐
-
3.代理模式
3.行为型模式
定义:关注于对象之间的职责分配和它们之间的通信。描述了不同的对象和类如何协作以实现更大的功能。行为型模式关注的是类和对象的交互以及职责。
-
1.观察者模式 ⭐
-
2.策略模式
-
3.职责链模式
-
4.迭代器模式
4.结构型模式和行为型模式的区别
总的来说,结构型模式和行为型模式分别解决软件设计中的不同方面的问题。结构型模式关注于对象和类的构造和组合,而行为型模式关注于对象间的交互和职责分配。
2.创建型模式
1.单例模式
1.定义
- 保证一个类仅有一个实例,并提供一个访问它的全局访问点。
2.JS代码实现
js
class Singleton {
constructor() {
if (!Singleton.instance) {
Singleton.instance = this
}
return Singleton.instance
}
}
const obj1 = new Singleton()
const obj2 = new Singleton()
console.log(obj1 === obj2) // true
上面的代码中,你通过
new Singleton()
创建一个实例,后续再创建实例的话返回的还是第一个创建的实例,这就保证了这个Singleton
类仅有一个实例,然后访问它的全局访问点就是new Singleton()
。
3.优点
-
1.唯一实例:单例模式确保一个类只有一个实例,避免重复创建资源。
-
2.全局访问点:单例模式为唯一的实例提供了一个全局访问点,使其他对象可以直接访问。
-
3.共享资源:由于只存在一个实例,所以它可以方便的共享数据,使得数据的访问和操作更为集中和一致。
4.缺点
- 可能导致代码之间的过度耦合,并可能导致不需要的长时间生存周期的对象。
5.应用
- 1.全局配置:如果你的应用程序需要一个集中的地方来保存和管理配置信息(例如API端点、主题颜色、功能标志等),单例模式可以帮助确保整个应用程序使用同一组配置数据。
js
class AppConfig {
// 静态属性存储单例实例
static instance;
config = {
apiEndpoint: "https://api.example.com",
themeColor: "blue",
featureFlag: true
};
constructor() {
if (!AppConfig.instance) {
AppConfig.instance = this;
}
return AppConfig.instance;
}
setConfig(key, value) {
this.config[key] = value;
}
getConfig(key) {
return this.config[key];
}
getAllConfig() {
return this.config;
}
}
// 使用配置单例
const appConfig1 = new AppConfig();
const appConfig2 = new AppConfig();
console.log(appConfig1 === appConfig2); // true,确保是同一个实例
// 获取API端点
console.log(appConfig1.getConfig("apiEndpoint")); // "https://api.example.com"
// 修改主题颜色
appConfig1.setConfig("themeColor", "red");
// 从另一个地方确认修改的效果
console.log(appConfig2.getConfig("themeColor")); // "red"
// 获取所有配置
console.log(appConfig1.getAllConfig());
- 2.全局缓存
js
class Cache {
constructor(){
if(!Cache.instance){
this.cacheObject = {}
Cache.instance = this
}
return Cache.instance
}
set(key,value){
this.cacheObject[key] = value
}
get(key){
return this.cacheObject[key]
}
remove(key){
delete this.cacheObject[key]
}
clear(){
this.cacheObject = {}
}
}
const cacheInstance = new Cache()
cacheInstance.set('user_1',{name:'星空海绵',age:18})
console.log(cacheInstance.get('user_1'));
const anotherCacheInstance = new Cache()
console.log(cacheInstance.get('user_1'));
console.log(cacheInstance === anotherCacheInstance); // true
- 3.网页登录浮窗
js
class LoginModal {
constructor() {
if (LoginModal.instance) {
return LoginModal.instance;
}
this.modal = this.createModal();
LoginModal.instance = this;
}
createModal() {
const modal = document.createElement('div');
modal.innerHTML = `
<div class="login-modal">
<div class="login-content">
<h2>Login</h2>
<input type="text" placeholder="Username" />
<input type="password" placeholder="Password" />
<button>Submit</button>
</div>
</div>
`;
modal.style.display = 'none'; // Initially hidden
document.body.appendChild(modal);
return modal;
}
show() {
this.modal.style.display = 'block';
}
hide() {
this.modal.style.display = 'none';
}
}
// 使用
const login1 = new LoginModal();
login1.show();
const login2 = new LoginModal();
console.log(login1 === login2); // true
- 4.线程池
js
// 创建一个内联的Web Worker
const workerScript = `
self.onmessage = function(event) {
const result = event.data * 2;
self.postMessage(result);
};
`;
const workerBlob = new Blob([workerScript], { type: 'application/javascript' });
const workerUrl = URL.createObjectURL(workerBlob);
class ThreadPool {
constructor(size) {
this.size = size;
this.tasks = [];
this.workers = [];
for (let i = 0; i < this.size; i++) {
const worker = new Worker(workerUrl);
worker.onmessage = (event) => {
if (this.tasks.length > 0) {
const nextTask = this.tasks.shift();
worker.postMessage(nextTask.data);
nextTask.resolve(event.data);
} else {
this.workers.push(worker);
}
};
this.workers.push(worker);
}
}
execute(data) {
return new Promise((resolve) => {
const worker = this.workers.pop();
if (worker) {
worker.postMessage(data);
worker.onmessage = (event) => {
this.workers.push(worker);
resolve(event.data);
};
} else {
this.tasks.push({ data, resolve });
}
});
}
}
const threadPoolSingleton = (function () {
let instance = null;
return {
getThreadPool: function (size) {
if (!instance) {
instance = new ThreadPool(size);
}
return instance;
}
};
})();
// 使用线程池
const pool = threadPoolSingleton.getThreadPool(4);
pool.execute(5).then(result => console.log(result)); // 10
pool.execute(7).then(result => console.log(result)); // 14
3.结构型模式
1.装饰器模式
1.定义
- 在运行时向对象动态地添加新的行为或职责,而不修改其结构。
2.JS代码实现
js
class Coff {
cost() {
return 5
}
}
class Moka {
constructor(coff) {
this.coff = coff
}
cost() {
return this.coff.cost() + 7
}
}
class Latte {
constructor(coff) {
this.coff = coff
}
cost() {
return this.coff.cost() + 10
}
}
const coff = new Coff()
const moka = new Moka(coff)
const latte = new Latte(coff)
console.log(moka.cost());
console.log(latte.cost());
3.优点
-
1.更好的分离和职责划分:每个装饰器都只关心自己的功能,使得每个装饰器都遵循单一职责原则。
-
2.增强的扩展性:可以在运行时动态地、透明地添加功能,而不是通过继承来做到这一点。
4.缺点
- 1.可能导致大量小类:对于每种装饰功能,你都需要一个新的具体装饰器类,这可能导致系统中有很多小的类。
5.应用
- 权限系统:可以通过装饰器来为用户添加不同的权限或角色,从而实现细粒度的权限控制。
4.行为型模式
1.观察者模式
1.定义
- 观察者模式用于在对象之间建立一种一对多的依赖关系,这样当一个对象的状态发生变化时,所有依赖于它的对象都会得到通知并自动更新。
2.JS代码实现
js
class Subject {
constructor() {
this._observers = []
}
subscribe(observer) {
this._observers.push(observer)
}
unsubscribe(observer) {
const index = this._observers.indexOf(observer)
if (index !== -1) {
this._observers.splice(index, 1)
}
}
notify(data) {
for (let observer of this._observers) {
observer.update(data)
}
}
}
class Observer {
constructor(name) {
this.name = name
}
update(data) {
console.log(`${this.name}收到数据${data}`);
}
}
const subject = new Subject()
const observer1 = new Observer('Jack')
const observer2 = new Observer('Marry')
subject.subscribe(observer1)
subject.subscribe(observer2)
subject.notify('Hello!')
subject.unsubscribe(observer2)
subject.notify('你好!')
3.优点
-
1.开放/封闭原则:允许你在不修改主题的情况下增加新的观察者。观察者可以任意增减,主题无需关心。
-
2.建立动态关系:主题和观察者之间的关系是动态建立的,可以在运行时增加或删除观察者,使得系统更加灵活。
-
3.解耦:观察者和被观察对象是抽象耦合的,增加、删除、修改观察者或者被观察对象都很方便,并且不会影响对方。
-
4.支持广播通信:一个主题可以有任意数量的观察者。只需要增加一次就可以通知所有观察者对象,减少了系统的复杂性。
4.缺点
-
1.可能导致性能问题:如果一个主题有大量的直接和间接观察者,通知所有观察者可能会花费大量时间,从而影响系统的性能。
-
2.难于追踪和调试:由于观察者模式提供了一种比较抽象和隐蔽的消息传递机制,因此在出现问题时可能很难追踪和调试。
-
3.意外的更新:某些情况下,观察者可能收到并不需要的更新,从而导致不必要的操作。
5.应用
-
1.模型-视图-控制器(MVC)架构:模型(Model)是主题,视图(View)是观察者。当模型发生变化时,通知视图进行更新。
-
2.实时数据展示:例如股票或其他实时数据流的显示,当数据发生变化时,相关的展示组件需要得到及时的更新。
2.发布订阅模式
1.定义
- 发布-订阅模式(Pub-Sub)是一种消息通信模式,用于在系统的不同部分之间传递特定的事件信息。这种模式类似于广播:一个组件(发布者)发送消息,但并不知道谁将接收它;其他组件(订阅者)可以订阅这些消息并对它们作出响应,而无需知道是哪个组件发布了这些消息。
2.JS代码实现
js
class EventBus {
constructor() {
this.subscribers = {}
}
// 注册事件和回调
subscribe(event, callback) {
if (!this.subscribers[event]) {
this.subscribers[event] = []
}
this.subscribers[event].push(callback)
}
// 取消订阅
unsubscribe(event, callback) {
if (!this.subscribers[event]) return
const index = this.subscribers[event].indexOf(callback)
if (index !== -1) {
this.subscribers[event].splice(index, 1)
}
}
// 发布事件
publish(event, data) {
if (!this.subscribers[event]) return
for (let callback of this.subscribers[event]) {
callback(data)
}
}
}
const eventBus = new EventBus()
function displayData(data) {
console.log(`收到数据:${data}`);
}
eventBus.subscribe('dataReceived', displayData)
eventBus.publish('dataReceived', 'Hello!')
3.优点
-
1.解耦:发布者和订阅者之间没有直接的依赖关系。这提高了系统的灵活性和可扩展性。
-
2.动态:你可以在运行时动态地添加或删除订阅者,或改变发布者发布的消息。
-
3.多对多关系:多个发布者可以发送消息给多个订阅者。
4.缺点
- 1.调试困难:由于发布者和订阅者之间的解耦,可能导致在出现问题时难以找到错误的源头。
观察者模式和发布订阅模式的区别?
-
观察者模式:在此模式中,有两种主要的参与者:主题和观察者。主题维护了一组观察者,当主题的状态发生改变时,它会通知所有注册的观察者。
-
发布-订阅模式:此模式使用一个中介对象,通常被称为"调度中心"或"事件通道"。订阅者订阅事件,发布者发布事件,但它们不直接互相通知。而是通过调度中心来管理通知。