Javascript常用设计模式

设计模式

1、创建型

  • 工厂模式
  • 单例模式
  • 原型链模式

2、结构型

  • 适配器模式
  • 代理模式
  • 装饰器模式

3、行动型

  • 观察者模式
  • 发布-订阅模式
  • 策略模式

如下举例说明各个常见模式

1、创建型模式(单例模式、工厂模式、原型链模式)

javascript 复制代码
// 1、设计模型单例模式,
// !使用场景:全局模态框、全局状态管理、全局配置项等

class  User {
    constructor(name) {
        if(User.instance) {
            return User.instance
        }
        this.name = name
        User.instance = this
    }   
}
const user1 = new User('张三');
const user2 = new User('李四');

console.log(user1 === user2); // true

// 2、工厂模式Factory Pattern,
// !使用场景:根据不同的条件创建不同的对象,封装复杂对象创建、组件库、接口返回不同类型数据结构等
class Factory {}
class Phone extends Factory {}
class Computer extends Factory {};

function createProduct(type, name) {
    switch(type) {
        case 'phone':
            return new Phone(name);
        case 'computer':
            return new Computer(name);
        default:
            throw new Error('未知类型');
    }   
}
const phone = createProduct('phone', 'iPhone');
const computer = createProduct('computer', 'MacBook');
console.log(phone); // Phone { name: 'iPhone' }
console.log(computer); // Computer { name: 'MacBook' }

// 3、原型链模式 Prototype Pattern,
// !使用场景:对象之间存在继承关系、需要共享方法和属性等
function Person(name) { 
    this.name = name
}
Person.prototype.sayName = function() {
    console.log(this.name);
}
const person1 = new Person('张三');
const person2 = new Person('李四');
person1.sayName();  // 张三
person2.sayName();  // 李四
console.log(person1.__proto__ === Person.prototype); // true
console.log(person2.__proto__ === Person.prototype); // true

2、结构型(适配器模式、代理模式、装饰器模式)

js 复制代码
// 结构型模式
// 1、设计适配器模式
// 适配器模式(Adapter Pattern)是一种结构型设计模式,它允许将一个类的接口转换成客户希望的另一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。
// !使用场景:接口兼容、老项目改造、第三方SDK适配等
const  oldInterface = {
    getName:()=>{
        console.log('这是旧接口的方法1');
    }

}

function adapter(oldInterface) {
    return {
        getName: oldInterface.getName,
        getAge: () => {
            console.log('这是适配器新增的方法2');
        }
    }   
}

const newInterface = adapter(oldInterface);
newInterface.getName(); // 这是旧接口的方法1
newInterface.getAge(); // 这是适配器新增的方法2

// 2、代理模式Proxy Pattern,
// 作用:控制访问对象、做拦截、增强对象功能等
// !使用场景:权限控制、性能优化、日志记录, vue3响应式系统等
const  user = {
    name: '张三',
    age: 18
}
const  proxyUser = new Proxy(user, {
    get(target, prop) {
        console.log(`访问了属性${prop}`);
        return target[prop];
    },
    set(target, prop, value) {
        console.log(`修改了属性${prop},新值为${value}`);       
        target[prop] = value;
        return true;
    }   
});
console.log(proxyUser.name); // 访问了属性name  张三
proxyUser.age = 20; // 修改了属性age,新值为20
console.log(proxyUser.age); // 访问了属性age  20

// 3、装饰器模式Decorator Pattern,
// 作用:动态地给对象添加功能,而不改变其结构
// !使用场景:函数增强、组件增强、日志记录等
function log(target, name, descriptor) {
    const original = descriptor.value; 
    descriptor.value = function(...args) {
        console.log(`调用了方法${name},参数为${JSON.stringify(args)}`);
        return original.apply(this, args);
    }
    return descriptor;
}
        
function  testFunction() {console.log('这是一个装饰器函数');}

const testDecorator = log(testFunction, 'testFunction', { value: testFunction });
testDecorator.value(); // 调用了方法testFunction,参数为[]  这是一个装饰器函数

小结差异:

模式 相同 不同
代理模式 控制访问 图片懒加载、占位、Vue3响应式原理、权限控制
装饰器模式 控制访问 功能增强

3、行为型(发布-订阅模式、观察者模式、策略模式)

js 复制代码
// 行为模式:对象之间怎么通信
// 1、发布订阅模式Publish-Subscribe Pattern,
// 作用:对象之间通过发布订阅机制进行通信,实现解耦合
// !使用场景:事件系统、消息队列、组件通信等

const evntBus = {
    events: {},
    subscribe(event, callback) {
        if (!this.events[event]) {
            this.events[event] = []
        }
        this.events[event].push(callback)
    },
    publish(event, data) {
        if (this.events[event]) {
            this.events[event].forEach((callback) => callback(data))
        }
    }
}

evntBus.subscribe('testEvent', (data) => {
    console.log(`接收到事件testEvent,数据为${data}`)
})
evntBus.publish('testEvent', 'Hello World') // 接收到事件testEvent,数据为Hello World

evntBus.subscribe('testEvent1', (data) => {
    console.log(`又接收到事件testEvent,数据为${data}`)
})
evntBus.publish('testEvent', 'Hello Again')
// 接收到事件testEvent,数据为Hello Again
// 又接收到事件testEvent,数据为Hello Again finally

// 2、观察者模式Observer Pattern,
// 作用:对象之间通过观察者模式进行通信,实现一对多关系,一个
// !使用场景:数据绑定、事件监听、状态管理等
// !区别:发布订阅模式是通过事件中心进行通信,观察者模式是直接通过对象之间的引用进行通信,发布订阅模式更灵活-完全解耦,观察者模式更简单直接-松耦合
class Subject {
    constructor() {
        this.observers = [] // 观察者数组
    }
    add(ob) {
        this.observers.push(ob) // 添加观察者
    }
    notify(data) {
        this.observers.forEach((ob) => ob.update(data)) // 通知所有观察者
    }
}
const subject = new Subject();
subject.add({
    update(data) {
        console.log(`观察者1接收到数据${data}`)
    }
})
subject.notify('Hello Observer') // 观察者1接收到数据Hello Observer

subject.add({
    update(data) {
        console.log(`观察者2接收到数据${data}`)
    }
})
subject.notify('Hello Again')
// 观察者1接收到数据Hello Again
// 观察者2接收到数据Hello Again finally     

// 4、策略模式Strategy Pattern,
// 作用:定义一系列算法,将每个算法封装起来,并使它们可以互换
// !使用场景:表单验证、数据加密、排序算法等
const strategies = {
    isNonEmpty(value, errorMsg) {
        if (value === '') {
            return errorMsg
        }
    },
    isNumber(value, errorMsg) {
        if (typeof value !== 'number') {
            return errorMsg
        }
    }
}
function validate(value, rules) {
    for (let i = 0; i < rules.length; i++) {
        const rule = rules[i]
        const strategy = strategies[rule.strategy]
        if (strategy) {
            const errorMsg = strategy(value, rule.errorMsg)
            if (errorMsg) {
                return errorMsg
            }
        }
    }
    return null
}
const rules = [
    { strategy: 'isNonEmpty', errorMsg: '不能为空' },
    { strategy: 'isNumber', errorMsg: '必须是数字' }
]
const errorMsg = validate('', rules)     // 不能为空    
console.log(errorMsg); // 不能为空
const errorMsg2 = validate('123', rules) // 必须是数字
console.log(errorMsg2); // 必须是数字

// 比较简单的策略模式
const strategies2 = {
    A:(score)=>score >=90,
    B:(score)=>score >=80 && score <90,
    C:(score)=>score >=70 && score <80,
    D:(score)=>score >=60 && score <70,
    E:(score)=>score <60
}
function getGrade(score) {
    for (let key in strategies2) {
        if (strategies2[key](score)) {
            console.log(`成绩为${key}`);
            return key
        }
    }
    return '未知'
}
getGrade(95) // A
getGrade(85) // B
getGrade(75) // C
getGrade(65) // D
getGrade(55) // E
相关推荐
风骏时光牛马1 小时前
C Shell脚本编程与系统管理技术实践指南
javascript
烛衔溟1 小时前
TypeScript this 参数类型与全局 this
javascript·ubuntu·typescript
qq_12084093712 小时前
Three.js 工程向:GLTFLoader 管线、Draco/KTX2 与资源管理
开发语言·javascript·ecmascript
billy_huang2 小时前
Capacitor的基本使用
javascript·android studio
研☆香2 小时前
聊一聊js中的正则表达式的应用
前端·javascript·正则表达式
qq_12084093713 小时前
Three.js 工程向:Clock、deltaTime 与固定步长主循环
开发语言·javascript·ecmascript
用户11481867894843 小时前
Vosk-Browser 实现浏览器离线语音转文字
前端·javascript
军军君013 小时前
数字孪生监控大屏实战模板:云数据中心展示平台
前端·javascript·vue.js·typescript·前端框架·es6·echarts
吴声子夜歌3 小时前
Vue3——使用axios实现Ajax请求
前端·javascript·ajax·axios