设计模式
前端开发中常见的设计模式以及它们的应用?
单例模式(Singleton Pattern):确保一个类只有一个实例,并提供一个全局访问点。在前端开发中,可以使用单例模式来管理全局状态,例如 Vuex 中的 Store 对象就是一个单例。
工厂模式(Factory Pattern):定义一个用于创建对象的接口,让子类决定实例化哪一个类。在前端开发中,可以使用工厂模式来创建不同类型的组件,例如 React 中的 createElement 函数就是一个工厂函数。
观察者模式(Observer Pattern):定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。在前端开发中,可以使用观察者模式来实现组件之间的通信,例如 Vue 中的事件机制就是基于观察者模式实现的。
装饰器模式(Decorator Pattern):动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。在前端开发中,可以使用装饰器模式来扩展组件的功能,例如 React 中的 Higher-Order Component 就是一种装饰器模式。
适配器模式(Adapter Pattern):将一个类的接口转换成客户希望的另外一个接口。在前端开发中,可以使用适配器模式来兼容不同的 API,例如使用 Axios 封装一个统一的 HTTP 请求适配器。
什么是单例模式并使用JS实现?
单例模式是一种创建型设计模式,它保证一个类只有一个实例,并提供一个全局访问点。这在需要全局访问点的场景中非常有用,例如全局缓存、全局配置等。
JavaScript中可以依赖闭包实现:
// ES6 Class
class Singleton {
constructor(){
if(typeof Singleton.instance === 'object'){
return Singleton.instance;
}
Singleton.instance = this;
return this;
}
}
// ES5
(function Singleton(){
let instance;
function createInstance(){
const obj = new Object();
return obj
}
return {
getInstance: function(){
if(!instance){
instance = createInstance()
}
return instance
}
}
})()
观察者模式和发布/订阅模式的区别以及各自的应用场景?
观察者模式和发布/订阅模式都是行为型设计模式,它们都用于处理对象之间的通信,但是它们之间有一些关键的区别。
- 观察者模式:在观察者模式中,有一个被观察者(或主题)和多个观察者(或订阅者)。当被观察者的状态发生变化时,它会通知所有的观察者。观察者模式是一对多的关系。例如,当一个对象的状态改变需要通知其他对象时,可以使用观察者模式。在JavaScript中,事件监听器就是观察者模式的一种实现。
class Subject {
constructor() {
this.observers = [];
}
addObserver(observer) {
this.observers.push(observer);
}
removeObserver(observer) {
this.observers = this.observers.filter((item) => item !== observer);
}
notifyObservers(data) {
this.observers.forEach((observer) => 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("observer1");
const observer2 = new Observer("observer2");
subject.addObserver(observer1);
subject.addObserver(observer2);
subject.notifyObservers("hello"); // observer1收到消息:hello observer2收到消息:hello
- 发布/订阅模式:在发布/订阅模式中,有一个消息中心(或事件通道)、多个发布者和订阅者。订阅者可以订阅消息中心的消息,当消息中心有新的消息时,它会通知所有的订阅者。发布/订阅模式是一种解耦的方式,发布者和订阅者之间没有直接的联系。例如,当一个对象的状态改变需要通知其他对象时,也可以使用发布/订阅模式。在JavaScript中,EventEmitter就是发布/订阅模式的一种实现。
class EventEmitter {
constructor() {
this.events = {};
}
on(event, listener) {
if (!this.events[event]) {
this.events[event] = [];
}
this.events[event].push(listener);
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(listener => {
listener(args);
});
}
}
off(event, listener) {
if (this.events[event]) {
const index = this.events[event].indexOf(listener);
if (index > -1) {
this.events[event].splice(index, 1);
}
}
}
}
const eventEmitter = new EventEmitter();
function listener1(data) {
console.log('listener1', data[0]);
}
function listener2(data) {
console.log('listener2', data[0]);
}
eventEmitter.on('event', listener1);
eventEmitter.on('event', listener2);
eventEmitter.emit('event', 'hello'); // listener1 hello listener2 hello
eventEmitter.off('event', listener1);
eventEmitter.emit('event', 'world'); // listener2 world
总的来说,观察者模式和发布/订阅模式都是用于处理对象之间的通信,但是它们的实现方式和使用场景有所不同。观察者模式是一对多的关系,而发布/订阅模式是多对多的关系。
策略模式以及它如何优化分支?
策略模式(Strategy Pattern)是一种行为设计模式,它定义一系列的算法,把它们一个个封装起来,目的就是将算法的使用与算法的实现分离开来。
// 多分支
function doSomething(condition) {
if(condition === 'A'){
console.log('do something for A')
}else if (condition === 'B'){
console.log('do something for B')
}else if (condition === 'C'){
console.log('do something for C')
}
}
// 策略模式
const strategies = {
A: function() {
console.log('do something for A')
},
B: function() {
console.log('do something for B')
},
c: function() {
console.log('do something for C')
}
}
function doSomething(condition) {
strategies[condition]()
}
工厂模式和抽象工厂模式在前端的应用?
工厂模式和抽象工厂模式都是创建型设计模式,它们的主要目的是封装对象的创建过程,使得对象的创建和使用可以分开。
工厂模式:工厂模式是一种创建型设计模式,它定义了一个用于创建对象的接口,但由子类决定要实例化的类是哪一个。这种模式的主要优点是,它可以隐藏对象的创建细节,使得代码更容易维护。在前端开发中,工厂模式常常用于创建和管理UI组件。
抽象工厂模式:抽象工厂模式是一种创建型设计模式,它提供一个接口,用于创建相关或依赖对象的家族,而不需要明确指定具体类。这种模式的主要优点是,它可以确保一组相关的对象被一起创建,而不需要知道具体的实现细节。在前端开发中,抽象工厂模式常常用于创建和管理UI组件的样式。
总的来说,工厂模式和抽象工厂模式都可以帮助我们更好地组织和管理代码,使得代码更易于理解和维护。
工厂模式示例:
// 定义一个工厂函数
function createButton(type) {
if (type === 'submit') {
return new SubmitButton();
} else if (type === 'reset') {
return new ResetButton();
} else {
throw new Error('Invalid button type');
}
}
// 定义两个按钮类
class SubmitButton {
render() {
console.log('Render a submit button');
}
}
class ResetButton {
render() {
console.log('Render a reset button');
}
}
// 使用工厂函数创建按钮
let submitButton = createButton('submit');
let resetButton = createButton('reset');
submitButton.render(); // 输出 "Render a submit button"
resetButton.render(); // 输出 "Render a reset button"
抽象工厂模式示例:
// 定义一个抽象工厂
class ThemeFactory {
createButton() {
throw new Error('This is an abstract method');
}
}
// 定义两个具体的工厂
class LightThemeFactory extends ThemeFactory {
createButton() {
return new LightButton();
}
}
class DarkThemeFactory extends ThemeFactory {
createButton() {
return new DarkButton();
}
}
// 定义两个按钮类
class LightButton {
render() {
console.log('Render a light button');
}
}
class DarkButton {
render() {
console.log('Render a dark button');
}
}
// 使用抽象工厂创建按钮
let lightFactory = new LightThemeFactory();
let darkFactory = new DarkThemeFactory();
let lightButton = lightFactory.createButton();
let darkButton = darkFactory.createButton();
lightButton.render(); // 输出 "Render a light button"
darkButton.render(); // 输出 "Render a dark button"