JavaScript设计模式是一组被广泛使用的软件设计解决方案,用于解决代码复杂性、可维护性、重用性和扩展性等方面的问题。本文将介绍常见的JavaScript设计模式,并使用React-Hook对每种设计模式进行实践来帮助大家理解。
1. 单例模式
单例模式是一种创建型模式,用于确保类只有一个实例,并提供全局访问点。在JavaScript中,可以通过将对象字面量封装在函数闭包中来实现单例模式。
const Singleton = (function() {
let instance;
function createInstance() {
const object = { name: 'John' };
return object;
}
return {
getInstance: function() {
if (!instance) {
instance = createInstance();
}
return instance;
}
};
})();
console.log(Singleton.getInstance()); // { name: 'John' }
console.log(Singleton.getInstance()); // { name: 'John' }
在React中,单例模式可以用于创建共享状态的上下文对象。例如,以下代码演示如何使用React Hook创建一个计数器上下文对象。
import { createContext, useContext, useState } from 'react';
const CounterContext = createContext(null);
export const useCounter = () => useContext(CounterContext);
export const CounterProvider = ({ children }) => {
const [count, setCount] = useState(0);
return (
<CounterContext.Provider value={{ count, setCount }}>
{children}
</CounterContext.Provider>
);
};
使用上述代码,我们可以在React应用程序中使用useCounter
钩子来访问计数器状态。
2. 观察者模式
观察者模式是一种行为型模式,用于在对象之间建立一对多的依赖关系,当一个对象状态发生变化时,所有依赖该对象的对象都会被通知。在JavaScript中,可以使用回调函数或事件触发器来实现观察者模式。
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
const index = this.observers.indexOf(observer);
if (index > -1) {
this.observers.splice(index, 1);
}
}
notify() {
this.observers.forEach(observer => observer.update());
}
}
class Observer {
update() {
console.log('The subject has been updated');
}
}
const subject = new Subject();
const observer = new Observer();
subject.attach(observer);
subject.notify(); // The subject has been updated
subject.detach(observer);
在React中,观察者模式可以用于处理组件之间的状态同步。例如,以下代码演示如何使用React Hook创建一个计数器组件,当计数器值发生变化时,通知所有订阅组件。
import { createContext, useContext, useState, useEffect } from 'react';
const CounterContext = createContext(null);
export const useCounter = () => useContext(CounterContext);
export const CounterProvider = ({ children }) => {
const [count, setCount] = useState(0);
const [subscribers, setSubscribers] = useState([]);
useEffect(() => {
subscribers.forEach(subscriber => subscriber());
}, [count, subscribers]);
const subscribe = callback => {
setSubscribers(subscribers => [...subscribers, callback]);
};
const unsubscribe = callback => {
setSubscribers(subscribers =>
subscribers.filter(subscriber => subscriber !== callback),
);
};
return (
<CounterContext.Provider
value={{ count, setCount, subscribe, unsubscribe }}
>
{children}
</CounterContext.Provider>
);
};
export const CounterDisplay = () => {
const { count, subscribe, unsubscribe } = useCounter();
useEffect(() => {
const callback = () => console.log(`The count is ${count}`);
subscribe(callback);
return () => unsubscribe(callback);
}, [count, subscribe, unsubscribe]);
return <p>Count: {count}</p>;
};
export const CounterButton = () => {
const { count, setCount } = useCounter();
const handleClick = () => setCount(count => count + 1);
return <button onClick={handleClick}>Increment</button>;
};
使用上述代码,我们可以在React应用程序中创建一个计数器上下文,同时使用CounterDisplay
和CounterButton
组件订阅该上下文。每次计数器值发生变化时,所有订阅组件都会得到通知。
3. 工厂模式
工厂模式是一种创建型模式,用于封装对象的实例化过程,从而避免直接使用new
操作符创建对象。在JavaScript中,可以使用工厂函数或构造函数来实现工厂模式。
class Product {
constructor(name) {
this.name = name;
}
getName() {
return this.name;
}
}
class ProductFactory {
create(name) {
return new Product(name);
}
}
const factory = new ProductFactory();
const product = factory.create('Apple');
console.log(product.getName()); // Apple
在React中,工厂模式可以用于创建具有相同结构的组件。例如,以下代码演示如何使用React Hook创建一个可重用的按钮工厂函数。
import React from 'react';
const Button = ({ text, onClick }) => (
<button onClick={onClick}>{text}</button>
);
export const createButton = ({ text, onClick }) => (
<Button text={text} onClick={onClick} />
);
使用上述代码,我们可以在React应用程序中使用createButton
工厂函数创建任意数量的按钮组件,每个按钮组件都具有相同的结构和属性。
import React from 'react';
import { createButton } from './ButtonFactory';
const IncrementButton = createButton({
text: 'Increment',
onClick: () => console.log('Increment')
});
const DecrementButton = createButton({
text: 'Decrement',
onClick: () => console.log('Decrement')
});
const App = () => (
<>
<IncrementButton />
<DecrementButton />
</>
);
4. 适配器模式
适配器模式是一种结构型模式,用于将一个接口转换为另一个接口,以满足不同的客户端需求。在JavaScript中,可以使用对象适配器或类适配器来实现适配器模式。
class Adaptee {
specificRequest() {
return 'Specific request';
}
}
class Target {
request() {
return 'Default request';
}
}
class Adapter extends Target {
constructor(adaptee) {
super();
this.adaptee = adaptee;
}
request() {
return this.adaptee.specificRequest();
}
}
const target = new Target();
console.log(target.request()); // Default request
const adaptee = new Adaptee();
const adapter = new Adapter(adaptee);
console.log(adapter.request()); // Specific request
在React中,适配器模式可以用于将不同类型的数据转换为React组件所需的数据格式。例如,以下代码演示如何使用React Hook创建一个数据适配器。
import React, { useState, useEffect } from 'react';
const fetchData = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/users');
const data = await response.json();
return data;
};
const adaptData = data =>
data.map(user => ({ id: user.id, name: user.name, email: user.email }));
export const useUsers = () => {
const [users, setUsers] = useState([]);
useEffect(() => {
fetchData().then(data => setUsers(adaptData(data)));
}, []);
return users;
};
使用上述代码,我们可以在React应用程序中使用useUsers
自定义Hook获取用户数据,该Hook将从API中获取的用户数据转换为React组件所需的格式。
import React from 'react';
import { useUsers } from './UserAdapter';
const UserList = () => {
const users = useUsers();
return (
<ul>
{users.map(user => (
<li key={user.id}>{user.name} ({user.email})</li>
))}
</ul>
);
};
5. 观察者模式
观察者模式是一种行为型模式,用于建立对象之间的一对多依赖关系,当一个对象状态发生变化时,它的所有依赖对象都会收到通知。在JavaScript中,可以使用发布-订阅模式或回调函数来实现观察者模式。
class Subject {
constructor() {
this.observers = [];
}
attach(observer) {
this.observers.push(observer);
}
detach(observer) {
this.observers = this.observers.filter(obs => obs !== observer);
}
notify() {
this.observers.forEach(observer => observer.update());
}
}
class Observer {
constructor(subject) {
this.subject = subject;
this.subject.attach(this);
}
update() {
console.log('Observer updated');
}
detach() {
this.subject.detach(this);
}
}
const subject = new Subject();
const observer1 = new Observer(subject);
const observer2 = new Observer(subject);
subject.notify(); // Observer updated Observer updated
observer1.detach();
subject.notify(); // Observer updated
在React中,观察者模式可以用于在组件之间共享状态并响应状态变化。以下是使用React Hook实现观察者模式的示例代码:
import React, { createContext, useContext, useState } from 'react';
const ObserverContext = createContext(null);
export const ObserverProvider = ({ children }) => {
const [state, setState] = useState({});
const attach = observer => {
setState({ ...state, [observer.id]: observer });
};
const detach = observer => {
const { [observer.id]: omit, ...rest } = state;
setState(rest);
};
const notify = () => {
Object.values(state).forEach(observer => observer.update());
};
const value = { attach, detach, notify };
return (
<ObserverContext.Provider value={value}>
{children}
</ObserverContext.Provider>
);
};
export const useObserver = update => {
const { attach, detach, notify } = useContext(ObserverContext);
const [id] = useState(Math.random().toString());
const observer = { id, update };
attach(observer);
useEffect(() => {
return () => detach(observer);
}, [observer]);
return notify;
};
使用上述代码,我们可以在React应用程序中使用ObserverProvider
组件来创建一个全局的观察者容器,该容器可以用于存储和管理所有观察者对象。然后,我们可以使用useObserver
自定义Hook在React组件中创建一个观察者,并在需要时调用notify
函数来通知所有观察者。
import React from 'react';
import { ObserverProvider, useObserver } from './Observer';
const Counter = () => {
const [count, setCount] = useState(0);
const notify = useObserver(() => setCount(count + 1));
const handleClick = () => {
notify();
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
const App = () => {
return (
<ObserverProvider>
<Counter />
<Counter />
</ObserverProvider>
);
};
在上面的示例中,我们使用ObserverProvider
组件来创建一个全局的观察者容器,并在Counter
组件中使用useObserver
自定义Hook来创建一个观察者,该观察者会在每次单击按钮时通知所有观察者更新计数器的状态。由于Counter
组件在ObserverProvider
组件的下方调用了两次,因此在单击按钮时,所有计数器都将更新其状态并显示新的计数值。