【字节前端-理论篇】前端设计模式应用 | 豆包MarsCode AI刷题

42 阅读6分钟

一、浏览器中的设计模式

1. 单例模式 (Singleton Pattern)

概念

单例模式确保一个类只有一个实例,并提供全局访问点。在浏览器环境中,单例模式通常用于那些需要全局共享资源的场景,例如全局配置、共享状态、事件管理器等。

模式结构

image.png

image.png

image.png

使用场景
  • 配置管理(例如应用程序的全局配置对象)。
  • 单一的事件监听器(例如全局事件处理机制)。
  • 路由管理器或全局状态管理器。
优缺点
  • 优点

    • 资源共享:确保全局唯一的资源共享,避免重复实例化。
    • 全局访问:方便从应用的任何地方访问单例对象。
  • 缺点

    • 测试难度:由于单例模式在全局存在唯一实例,可能会导致单元测试时难以控制该实例的状态。
    • 隐式依赖:全局状态可能会被其他部分的代码意外修改,增加了系统的耦合度。
实例

在浏览器中,常见的单例模式实现是全局配置对象或事件管理器:

js复制代码
const Config = (function() {
    let instance;
    
    function createInstance() {
        return {
            apiUrl: "https://api.example.com",
            timeout: 5000
        };
    }

    return {
        getInstance: function() {
            if (!instance) {
                instance = createInstance();
            }
            return instance;
        }
    };
})();

// 使用
const config1 = Config.getInstance();
const config2 = Config.getInstance();
console.log(config1 === config2);  // true,确保只有一个实例

2. 观察者模式 / 发布-订阅模式 (Observer/Publisher-Subscriber Pattern)

概念

观察者模式允许对象之间一对多的依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。在浏览器环境中,这通常用于事件机制、数据绑定和通知系统。

模式结构

image.png

使用场景
  • 事件监听:浏览器中的 DOM 事件机制。
  • 数据绑定:框架如 Vue.js 中的数据变动通知视图更新。
  • 发布-订阅系统:例如消息通知、状态管理系统等。
优缺点
  • 优点

    • 松耦合:对象之间的依赖是动态的,不需要明确的引用关系。
    • 扩展性好:可以轻松地添加新的观察者,而不需要修改已有代码。
  • 缺点

    • 内存泄漏:如果不及时取消订阅,可能导致内存泄漏。
    • 性能问题:当观察者过多时,通知机制可能导致性能瓶颈。
实例

JavaScript中的事件监听机制就是一种典型的观察者模式实现:

js复制代码
// 观察者
function onMessageReceived(message) {
    console.log('Received message:', message);
}

// 发布者
let eventEmitter = new EventTarget();
eventEmitter.addEventListener('message', onMessageReceived);

// 发布事件
eventEmitter.dispatchEvent(new CustomEvent('message', { detail: 'Hello, World!' }));

二、JavaScript中的设计模式

1. 原型模式 (Prototype Pattern)

概念

原型模式通过复制现有的对象来创建新的对象,而不是通过构造函数实例化。JavaScript天生支持原型继承,允许一个对象通过复制现有对象来创建新的对象。

模式结构

image.png

使用场景
  • 对象实例化过程需要克隆现有对象时。
  • 创建具有共享属性或方法的对象,减少内存消耗。
优缺点
  • 优点

    • 内存节省:多个对象可以共享相同的方法和属性,避免重复创建。
    • 灵活性高:通过克隆对象,可以动态创建具有相似特征的对象。
  • 缺点

    • 复杂性:创建和管理对象的原型链可能会变得复杂,尤其是当原型链很深时。
    • 可能的混淆:如果对象的原型被更改,可能会影响到所有实例的行为。
实例

JavaScript中的原型继承就是原型模式的体现:

js复制代码
function Animal(name) {
    this.name = name;
}

Animal.prototype.speak = function() {
    console.log(this.name + ' makes a noise.');
};

const dog = Object.create(Animal.prototype);
dog.name = 'Dog';
dog.speak();  // Dog makes a noise.

2. 代理模式 (Proxy Pattern)

概念

代理模式为其他对象提供一种代理以控制对该对象的访问。通过代理可以实现对目标对象的延迟加载、缓存、权限控制等。

模式结构

image.png

使用场景
  • 懒加载:在需要时才加载对象的资源。
  • 权限控制:控制对某些操作的访问。
  • 缓存代理:在代理中缓存结果,避免重复计算。
优缺点
  • 优点

    • 控制访问:通过代理可以控制对对象的访问。
    • 灵活性:可以在不修改目标对象的情况下改变其行为。
  • 缺点

    • 增加复杂度:代理层的增加可能导致系统复杂度上升,尤其是多层代理时。
    • 性能开销:代理可能会引入额外的性能开销。
实例

浏览器中的 Proxy 对象就是代理模式的一个典型例子:

js复制代码
let person = {
    name: 'John',
    age: 30
};

let handler = {
    get: function(target, prop) {
        if (prop === 'name') {
            return `Mr. ${target[prop]}`;
        }
        return target[prop];
    }
};

let proxyPerson = new Proxy(person, handler);

console.log(proxyPerson.name);  // Mr. John
console.log(proxyPerson.age);   // 30

3. 迭代器模式 (Iterator Pattern)

概念

迭代器模式提供了一种顺序访问集合对象的方式,而无需暴露集合的内部结构。JavaScript中的数组和类数组对象(如 NodeList)都可以使用迭代器来访问。

模式结构

image.png

使用场景
  • 遍历数据:例如在数组或集合上迭代时。
  • 集合对象:当集合内部结构复杂时,使用迭代器模式可以简化外部访问方式。
优缺点
  • 优点

    • 统一接口:通过迭代器可以统一访问不同类型的数据集合。
    • 灵活性:可以通过改变迭代器的实现,轻松改变遍历方式。
  • 缺点

    • 可能的性能问题:对于非常大的数据集合,迭代可能会变得非常慢。
    • 复杂性:实现迭代器模式可能会增加一些额外的代码复杂度。
实例

JavaScript中常用的 for...of 循环其实就是基于迭代器模式实现的:

js复制代码
const numbers = [1, 2, 3, 4, 5];
const iterator = numbers[Symbol.iterator]();

console.log(iterator.next().value);  // 1
console.log(iterator.next().value);  // 2

三、前端框架中的设计模式

1. 代理模式 (Proxy Pattern)

在前端框架中,代理模式主要用于实现数据绑定、懒加载、缓存等功能。例如,Vue.js 和 React 在数据的观察和更新时,经常会使用代理模式来实现数据的自动同步更新。

2. 组合模式 (Composite Pattern)

概念

组合模式将对象组合成树形结构,以便用户可以像处理单个对象一样处理组合对象。它允许客户端通过递归组合的方式来处理复杂对象的集合。

模式结构

image.png

使用场景
  • UI组件树:例如在前端框架中,UI元素可以组合成复杂的树状结构,父组件和子组件都是树的节点。
  • DOM结构的树形管理:如文件系统、菜单等树形结构的管理。
优缺点
  • 优点

    • 统一接口:对叶节点和组合节点使用相同的接口进行操作,简化代码。
    • 灵活性:可以灵活地对树状结构进行组合和扩展。
  • 缺点

    • 结构复杂性:树形结构的管理可能导致系统设计上的复杂度提升。
    • 性能问题:树形结构的遍历和操作可能需要较多的资源,特别是树结构很大的时候。
实例

Vue中的组件