Javascript设计模式

7 种常见的 javascript 设计模式

工厂模式

JavaScript 中的工厂模式是一种创建对象的设计模式,它通过一个工厂函数或方法来创建和返回对象,而不是直接使用构造函数或类。工厂模式允许你封装对象的创建过程,并可以根据需要创建不同类型的对象实例。以下是一个简单的示例来说明 JavaScript 中的工厂模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 创建一个工厂函数来创建不同类型的汽车对象
function createCar(make, model) {
// 创建一个空对象
var car = {};

// 添加属性和方法到对象
car.make = make;
car.model = model;
car.start = function () {
console.log(`Starting the ${this.make} ${this.model}`);
};

// 返回创建的对象
return car;
}

// 使用工厂函数创建不同的汽车对象
var car1 = createCar("Toyota", "Camry");
var car2 = createCar("Honda", "Civic");

// 调用对象的方法
car1.start(); // 输出: Starting the Toyota Camry
car2.start(); // 输出: Starting the Honda Civic

在上面的示例中,createCar 函数是一个工厂函数,它接受参数 make 和 model,并返回一个新的汽车对象。每次调用 createCar 函数时,都会创建一个新的汽车对象,并可以为每个对象设置不同的属性值。这种方式使你可以轻松地创建多个相似类型的对象,而无需直接使用构造函数或类。

工厂模式在需要创建多个相似对象实例的情况下非常有用,它可以帮助你更好地组织和封装对象的创建逻辑。然后,你可以根据需要扩展工厂函数以创建不同类型的对象。

单例模式

JavaScript 中的单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。这可以用于限制特定类型的对象只有一个全局实例,以确保在整个应用程序中共享相同的状态和行为。以下是一个简单的示例来说明 JavaScript 中的单例模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 创建一个单例对象
var Singleton = (function () {
// 私有变量,只能在闭包内访问
var instance;

// 构造函数,创建单例实例
function SingletonClass() {
// 添加属性或方法到实例
this.property1 = "Singleton Property";
this.method1 = function () {
console.log("Singleton Method");
};
}

// 返回单例实例的方法
return {
getInstance: function () {
if (!instance) {
// 如果实例不存在,则创建一个新实例
instance = new SingletonClass();
}
return instance;
},
};
})();

// 使用单例模式创建对象
var singleton1 = Singleton.getInstance();
var singleton2 = Singleton.getInstance();

// 验证两个实例是否相同
console.log(singleton1 === singleton2); // 输出: true

// 访问单例对象的属性和方法
console.log(singleton1.property1); // 输出: Singleton Property
singleton1.method1(); // 输出: Singleton Method

在上面的示例中,我们通过一个立即执行函数(IIFE)创建了一个闭包,其中包含一个私有变量 instance 和一个构造函数 SingletonClassgetInstance 方法用于获取单例实例,如果实例不存在,则创建一个新实例,否则返回现有的实例。

使用单例模式可以确保在应用程序中只有一个全局实例,这对于管理共享状态和避免不必要的实例化非常有用。请注意,JavaScript 中的单例模式可以通过不同的实现方式来实现,上面的示例是其中一种常见的方式。

适配器模式

适配器模式(Adapter Pattern)是一种设计模式,用于解决不同接口或对象之间的兼容性问题。它允许一个对象(适配器)将其接口转换为另一个对象所期望的接口,从而使两者可以一起工作。适配器模式通常用于以下情况:

  1. 集成现有代码:当你需要使用一个已经存在的类,但其接口与你的需求不匹配时,可以创建一个适配器来使其与你的代码协同工作。

  2. 与第三方库集成:当你需要与一个外部库或服务进行交互,但其接口与你的应用程序不兼容时,可以使用适配器来将两者连接起来。

以下是一个简单的 JavaScript 适配器模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 假设有一个旧的类,它有不兼容的接口
class OldService {
request(data) {
console.log(`Old Service: ${data}`);
}
}

// 创建一个适配器,将旧的接口适配成新的接口
class Adapter {
constructor() {
this.oldService = new OldService();
}

newRequest(data) {
console.log("Adapter is converting the request...");
this.oldService.request(data);
}
}

// 使用适配器来调用旧的服务
const adapter = new Adapter();
adapter.newRequest("Adapter Pattern Demo");

在上面的示例中,OldService 是一个具有旧接口的类,而 Adapter 是一个适配器类,它将旧的接口适配成新的接口。通过适配器,我们可以使用 newRequest 方法来调用旧的服务,并在适配器中进行必要的转换。

适配器模式允许你在不修改现有代码的情况下,与不同接口的对象进行交互,提高了代码的可扩展性和复用性。这对于在多个组件之间集成和协同工作时非常有用,特别是在大型应用程序中。

装饰器模式

装饰器模式(Decorator Pattern)是一种结构性设计模式,用于动态地给对象添加新的行为或功能,而不需要修改其原始类。这种模式允许你通过将对象包装在装饰器类中来扩展其功能,从而实现透明地添加功能。在 JavaScript 中,装饰器模式通常使用函数或类来实现。

以下是一个简单的 JavaScript 装饰器模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 原始组件类
class Coffee {
cost() {
return 5;
}
}

// 装饰器类
class MilkDecorator {
constructor(coffee) {
this.coffee = coffee;
}

cost() {
return this.coffee.cost() + 2;
}
}

class SugarDecorator {
constructor(coffee) {
this.coffee = coffee;
}

cost() {
return this.coffee.cost() + 1;
}
}

// 使用装饰器来扩展原始组件
const coffee = new Coffee();
console.log(`Cost of Coffee: $${coffee.cost()}`);

const coffeeWithMilk = new MilkDecorator(coffee);
console.log(`Cost of Coffee with Milk: $${coffeeWithMilk.cost()}`);

const coffeeWithMilkAndSugar = new SugarDecorator(coffeeWithMilk);
console.log(
`Cost of Coffee with Milk and Sugar: $${coffeeWithMilkAndSugar.cost()}`
);

在上面的示例中,有一个原始的 Coffee 类,它表示一杯咖啡。然后有两个装饰器类 MilkDecoratorSugarDecorator,它们分别用于在咖啡上添加牛奶和糖的功能。通过创建装饰器对象并嵌套它们,可以透明地扩展原始组件的功能,而不需要修改原始组件类。

装饰器模式在不破坏现有代码结构的情况下,允许你动态地组合对象的功能。这在创建可复用的组件和在运行时添加或删除功能时非常有用。在 JavaScript 中,装饰器模式通常与类和原型继承一起使用。

代理模式

代理模式(Proxy Pattern)是一种结构性设计模式,它充当另一个对象的接口,以控制对该对象的访问。代理对象通常用于在访问实际对象之前或之后执行一些附加操作,例如延迟加载、访问控制、缓存等。在 JavaScript 中,代理模式可以使用对象或函数来实现。

以下是一个简单的 JavaScript 代理模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// 原始对象
class RealImage {
constructor(filename) {
this.filename = filename;
this.loadImage();
}

loadImage() {
console.log(`Loading image: ${this.filename}`);
}

display() {
console.log(`Displaying image: ${this.filename}`);
}
}

// 代理对象
class ImageProxy {
constructor(filename) {
this.filename = filename;
this.realImage = null;
}

display() {
if (this.realImage === null) {
this.realImage = new RealImage(this.filename);
}
this.realImage.display();
}
}

// 使用代理对象来控制访问原始对象
const image1 = new ImageProxy("image1.jpg");
const image2 = new ImageProxy("image2.jpg");

// 实际调用会延迟加载原始对象
image1.display(); // 加载并显示 "image1.jpg"
image2.display(); // 加载并显示 "image2.jpg"
image1.display(); // 直接显示 "image1.jpg"(已经加载过)

在上面的示例中,RealImage 是原始对象,代表加载和显示图像的功能。ImageProxy 是代理对象,它控制对实际图像的访问。当第一次调用代理对象的 display 方法时,它会延迟加载实际图像对象,并在加载完成后调用实际图像的 display 方法。在后续的调用中,代理对象会直接调用实际图像的 display 方法,因为图像已经加载过了。

代理模式在需要在访问实际对象之前或之后执行某些操作时非常有用,例如延迟加载、权限控制、缓存等。它可以帮助你实现更加灵活和可维护

观察者模式

观察者模式(Observer Pattern)是一种行为型设计模式,用于创建一种对象(称为主题或被观察者),它维护一组依赖于它的对象(称为观察者)的列表,当主题的状态发生变化时,会自动通知并更新所有的观察者。观察者模式是一种发布-订阅模式,用于实现对象之间的松耦合通信。

在 JavaScript 中,观察者模式常常用于以下情况:

  1. 当一个对象的状态改变需要通知其他对象,但你不知道这些对象是谁时。
  2. 当一个对象需要将自己的改变通知给多个对象,而不需要知道这些对象是谁时。
  3. 当一个对象需要与其他对象解耦,以便独立进行开发和维护时。

以下是一个简单的 JavaScript 观察者模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 主题(被观察者)类
class Subject {
constructor() {
this.observers = []; // 观察者列表
}

// 添加观察者
addObserver(observer) {
this.observers.push(observer);
}

// 移除观察者
removeObserver(observer) {
const index = this.observers.indexOf(observer);
if (index !== -1) {
this.observers.splice(index, 1);
}
}

// 通知所有观察者
notify() {
for (const observer of this.observers) {
observer.update();
}
}
}

// 观察者类
class Observer {
constructor(name) {
this.name = name;
}

// 观察者的更新方法
update() {
console.log(`${this.name} 收到通知并更新了。`);
}
}

// 创建主题对象
const subject = new Subject();

// 创建观察者对象
const observer1 = new Observer("观察者1");
const observer2 = new Observer("观察者2");

// 添加观察者到主题
subject.addObserver(observer1);
subject.addObserver(observer2);

// 主题发出通知,所有观察者会更新
subject.notify();

在上面的示例中,Subject 是主题类,它维护了一个观察者列表,并提供了添加、移除和通知观察者的方法。Observer 是观察者类,它具有一个 update 方法,用于在主题发出通知时执行更新操作。

观察者模式使得对象之间可以松散耦合,主题和观察者之间没有直接的依赖关系,可以随时添加或移除观察者。这种模式在实现事件处理、UI组件、数据绑定等方面都非常有用。

迭代器模式

迭代器模式(Iterator Pattern)是一种行为型设计模式,用于提供一种方法来访问一个聚合对象(如数组、集合、列表等),而不需要暴露该对象的内部表示。它把访问集合中的元素的责任委托给一个迭代器对象,使得我们可以在不了解集合内部结构的情况下逐个访问元素。

在 JavaScript 中,迭代器模式通常使用对象或函数来实现。ES6 中引入了内置的迭代器协议,使得创建自定义迭代器变得更加容易。

以下是一个简单的 JavaScript 迭代器模式的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// 自定义迭代器对象
class MyIterator {
constructor(data) {
this.index = 0;
this.data = data;
}

// 实现迭代器协议的 next 方法
next() {
if (this.index < this.data.length) {
return { value: this.data[this.index++], done: false };
} else {
return { done: true };
}
}
}

// 集合对象
class MyCollection {
constructor() {
this.data = [];
}

// 添加元素到集合
add(item) {
this.data.push(item);
}

// 创建并返回自定义迭代器
[Symbol.iterator]() {
return new MyIterator(this.data);
}
}

// 使用自定义迭代器遍历集合
const collection = new MyCollection();
collection.add("Item 1");
collection.add("Item 2");
collection.add("Item 3");

for (const item of collection) {
console.log(item);
}

在上面的示例中,MyIterator 是自定义的迭代器对象,实现了迭代器协议的 next 方法。MyCollection 是集合对象,它实现了 ES6 中的迭代器协议,通过 [Symbol.iterator] 方法创建并返回一个迭代器对象。

通过使用迭代器模式,我们可以遍历集合对象的元素,而不需要知道集合内部的实际结构。这样可以提高代码的灵活性和可复用性,使得不同类型的集合都可以通过相同的方式进行迭代。迭代器模式在处理数据集合、DOM节点列表等情况下非常有用。


Javascript设计模式
http://jhayes.cn/blog/911124684.html
作者
JHAYES
发布于
2022年4月13日
许可协议