JavaScript设计模式 -- 代理模式

在软件开发中,有时我们需要在访问某个对象时,增加额外的控制或处理逻辑,例如延迟加载、权限校验、日志记录或缓存等。**代理模式(Proxy Pattern)**正是为了解决这些问题而诞生的。它通过为目标对象提供一个代理对象,来间接控制对目标对象的访问,从而达到增强或保护目标对象功能的目的。

本文将详细介绍代理模式的基本概念、分类、以及在 JavaScript 中的多种实现示例,帮助你在实际项目中灵活运用代理模式。

代理模式简介

代理模式是一种结构型设计模式,其核心思想是在访问目标对象(真实对象)时,通过一个中间层(代理对象)来控制对真实对象的访问。代理对象与真实对象具有相同的接口,因此客户端无需改变代码即可使用代理对象。代理模式的主要作用包括:

  • 延迟加载:在真正需要使用对象时才创建或加载对象(虚拟代理)。
  • 权限校验:在调用真实对象方法前进行安全性检查(保护代理)。
  • 缓存:对真实对象的调用结果进行缓存,提高系统性能(缓存代理)。
  • 日志记录和监控:记录对真实对象的调用情况,便于调试和监控。

代理模式的分类

根据代理对象的用途和实现方式,代理模式主要可以分为以下几类:

  • 虚拟代理:用于延迟加载或创建开销较大的对象,在真正需要时才创建真实对象。
  • 保护代理:用于控制对真实对象的访问权限或进行额外的安全性检查。
  • 缓存代理:用于缓存真实对象的方法调用结果,避免重复计算或网络请求。
  • 远程代理:用于访问远程对象,为客户端隐藏分布式系统的复杂性。
  • 日志代理:用于记录对真实对象的操作,方便系统监控和调试。

在 JavaScript 中,由于其灵活的函数式编程特性,实现以上代理模式都非常方便。


JavaScript 中的代理模式实现示例

下面我们通过三个示例来展示如何在 JavaScript 中使用代理模式解决实际问题。

示例 1:虚拟代理(Lazy Proxy)

假设有一个图片加载器,加载高分辨率图片可能比较耗时,我们希望在图片真正需要展示时再进行加载,从而节省资源。使用虚拟代理可以在调用时延迟加载真实图片对象。

// 真实对象:高分辨率图片加载器
class HighResImage {
  constructor(url) {
    this.url = url;
    this.loadImage();
  }
  
  loadImage() {
    console.log(`开始加载高分辨率图片:${this.url}`);
    // 模拟耗时加载操作
    setTimeout(() => {
      console.log(`图片加载完成:${this.url}`);
    }, 2000);
  }
  
  display() {
    console.log(`显示图片:${this.url}`);
  }
}

// 虚拟代理:延迟创建 HighResImage 实例
class ImageProxy {
  constructor(url) {
    this.url = url;
    this.realImage = null;
  }
  
  display() {
    if (!this.realImage) {
      console.log('第一次调用 display(),创建真实图片对象...');
      this.realImage = new HighResImage(this.url);
    }
    this.realImage.display();
  }
}

// 客户端调用
const imageProxy = new ImageProxy('https://example.com/high-res.jpg');
imageProxy.display();
// 输出示例:
// 第一次调用 display(),创建真实图片对象...
// 开始加载高分辨率图片:https://example.com/high-res.jpg
// 显示图片: https://example.com/high-res.jpg
// (2秒后)图片加载完成: https://example.com/high-res.jpg

在此示例中,ImageProxy 代理对象在第一次调用 display() 时才会创建并加载真实图片对象,从而实现了延迟加载。


示例 2:保护代理(Protection Proxy)

在某些场景下,我们希望在调用真实对象方法之前进行权限检查,例如只有拥有管理员权限的用户才能执行敏感操作。保护代理可以在调用真实对象之前进行安全性验证。

// 真实对象:系统管理器
class AdminOperations {
  deleteUser(userId) {
    console.log(`删除用户:${userId}`);
  }
  
  updateSettings(settings) {
    console.log(`更新系统设置:${JSON.stringify(settings)}`);
  }
}

// 保护代理:进行权限校验
class AdminOperationsProxy {
  constructor(userRole) {
    this.userRole = userRole; // 用户角色,如 'admin' 或 'guest'
    this.adminOperations = new AdminOperations();
  }
  
  deleteUser(userId) {
    if (this.userRole !== 'admin') {
      console.log('权限不足,无法删除用户。');
      return;
    }
    this.adminOperations.deleteUser(userId);
  }
  
  updateSettings(settings) {
    if (this.userRole !== 'admin') {
      console.log('权限不足,无法更新设置。');
      return;
    }
    this.adminOperations.updateSettings(settings);
  }
}

// 客户端调用
const proxyForGuest = new AdminOperationsProxy('guest');
proxyForGuest.deleteUser(123);  // 输出:权限不足,无法删除用户。

const proxyForAdmin = new AdminOperationsProxy('admin');
proxyForAdmin.deleteUser(456);  // 输出:删除用户:456

通过保护代理,我们在调用敏感操作前进行了权限检查,避免了未经授权的操作。


示例 3:缓存代理(Caching Proxy)

对于某些开销较大的计算或数据请求,我们可以使用缓存代理保存结果,避免重复计算或网络请求,提高性能。

// 真实对象:模拟一个耗时计算函数
function heavyComputation(num) {
  console.log(`开始计算:${num}`);
  // 模拟复杂计算(这里使用简单的平方运算代替)
  let result = num * num;
  console.log(`计算完成:${num} 的平方是 ${result}`);
  return result;
}

// 缓存代理:为 heavyComputation 添加缓存功能
function cachingProxy(fn) {
  const cache = new Map();
  return function(num) {
    if (cache.has(num)) {
      console.log(`从缓存中获取结果:${num}`);
      return cache.get(num);
    }
    const result = fn(num);
    cache.set(num, result);
    return result;
  };
}

// 使用代理后的函数
const cachedComputation = cachingProxy(heavyComputation);

console.log(cachedComputation(5));  // 第一次计算,输出计算过程
console.log(cachedComputation(5));  // 第二次调用,从缓存中获取结果

在这个例子中,cachingProxy 函数包装了 heavyComputation,为相同输入值缓存结果,从而避免重复计算。


代理模式的优缺点

优点

  • 解耦客户端与真实对象:客户端只需通过代理对象进行交互,无需直接依赖真实对象。
  • 增强功能:在调用真实对象前后增加额外处理,如权限校验、延迟加载、缓存等。
  • 控制对象访问:可以对真实对象的访问进行统一控制,方便集中管理。

缺点

  • 增加系统复杂度:引入代理层可能使系统结构变得更复杂,增加维护成本。
  • 性能开销:代理对象需要额外的处理逻辑,在某些场景下可能会带来额外的性能开销。

总结

代理模式通过在客户端与真实对象之间增加一层代理,有效地控制了对真实对象的访问,并在此过程中实现了延迟加载、权限校验、缓存等多种扩展功能。本文通过虚拟代理、保护代理和缓存代理三个示例,展示了如何在 JavaScript 中灵活运用代理模式来解决实际问题。

希望这篇文章能帮助你深入理解代理模式,并在项目中找到合适的应用场景。如果你有任何问题或建议,欢迎在评论区讨论交流!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/971988.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 之服务器配置和状态(MySQL Server Configuration and Status)

MySQL 之服务器配置和状态 1 MySQL 架构和性能优化 1.3 服务器配置和状态 设置 MySQL 服务的特性,可以通过 mysqld 服务选项,服务器系统变量和服务器状态变量这三个方面来进行设置和查看。 官方文档 https://dev.mysql.com/doc/refman/8.0/en/serve…

Linux的基础指令和环境部署,项目部署实战(下)

目录 上一篇:Linxu的基础指令和环境部署,项目部署实战(上)-CSDN博客 1. 搭建Java部署环境 1.1 apt apt常用命令 列出所有的软件包 更新软件包数据库 安装软件包 移除软件包 1.2 JDK 1.2.1. 更新 1.2.2. 安装openjdk&am…

LabVIEW无刷电机控制器检测系统

开发了一种基于LabVIEW的无刷电机控制器检测系统。由于无刷电机具有高效率、低能耗等优点,在电动领域有取代传统电机的趋势,而无刷电机的核心部件无刷电机控制器产量也在不断增长。然而,无刷电机控制器的出厂检测仍处于半自动化状态&#xff…

《仙台有树》里的馅料(序)

《仙台有树》一起追剧吧(二):馅料合集概览 ●德爱武美玩,全面发展 ●猜猜我是谁&真假美清歌 ●失忆的风还是吹到了仙台 ●霸道师徒强制收&你拜我,我拜你,师徒徒师甜蜜蜜 ●霸道总裁强制爱 ●仙台有…

网站搭建基本流程

需求分析: 实现网站搭建的过程:首先进行网站的需求性分析 网站可分为前台系统和后台系统,由不同的功能拆分为不同的模块 如下是一个电商网站可以拆分出的模块: 在编写代码前,我们要先对网站进行架构,通过…

反射机制的简单示例

一个使用反射机制的简单示例&#xff0c;这个示例将展示如何使用反射来实现一个通用的数据导出功能。 首先&#xff0c;让我们创建必要的项目结构和文件&#xff1a; 首先修改 pom.xml 添加依赖&#xff1a; <?xml version"1.0" encoding"UTF-8"?&…

Qt:多元素控件

目录 多元素控件介绍 QListWidget QTableWidget QTreeWidget 多元素控件介绍 多元素控件表示这个控件中包含了很多的元素&#xff0c;元素可能指的是字符串&#xff0c;也可以指的是更加复杂的数据结构、图片等等 Qt 中提供的多元素控件有: QListWidgetQListViewQTableW…

DeepSeek 助力 Vue 开发:打造丝滑的范围选择器(Range Picker)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

STL —— 洛谷字符串(string库)入门题(蓝桥杯题目训练)(一)

目录 一、B2109 统计数字字符个数 - 洛谷 算法代码&#xff1a; 1. 引入库和命名空间 2. 主函数 3. 读取输入 4. 变量初始化 5. 遍历字符串 6. 输出结果 7. 返回值 总结 评测记录&#xff1a; 二、B2110 找第一个只出现一次的字符 - 洛谷 方法一&#xff1a;算法代…

Golang GORM系列:GORM并发与连接池

GORM 是一个流行的 Go 语言 ORM&#xff08;对象关系映射&#xff09;库&#xff0c;用于简化数据库操作。它支持连接池和并发访问功能&#xff0c;这些功能对于高性能、高并发的应用场景非常重要。本文结合示例详细介绍gorm的并发处理能力&#xff0c;以及如何是哟个连接池提升…

C#之上位机开发---------C#通信库及WPF的简单实践

〇、上位机&#xff0c;分层架构 界面层 要实现的功能&#xff1a; 展示数据 获取数据 发送数据 数据层 要实现的功能&#xff1a; 转换数据 打包数据 存取数据 通信层 要实现的功能&#xff1a; 打开连接 关闭连接 读取数据 写入数据 实体类 作用&#xff1a; 封装数据…

Ubuntu24安装MongoDB(解压版)

目录 0.需求说明1.环境检查2.下载软件2.1.下载MongoDB服务端2.2.下载MongoDB连接工具(可略过)2.3.检查上传或下载的安装包 3.安装MongoDB3.1.编辑系统服务3.2.启动服务3.3.客户端连接验证3.3.1.创建管理员用户 4.远程访问4.1.开启远程访问4.2.开放防火墙 0.需求说明 问&#x…

《DeepSeek-V3:人工智能大语言模型》

《DeepSeek-V3:人工智能大语言模型》 1. 引言 我们介绍了 DeepSeek-V3,这是一个强大的专家混合 (MoE) 语言模型,总共有 671B 个参数,每个令牌激活了 37B。 为了实现高效的推理和具有成本效益的训练,DeepSeek-V3 采用了多头潜在注意力 (MLA) 和 DeepSeekMoE 架构,这些…

解锁机器学习核心算法 | K -近邻算法:机器学习的神奇钥匙

一、引言 今天我们继续学习机器学习核心算法 —— K - 近邻&#xff08;K-Nearest Neighbors&#xff0c;简称 KNN&#xff09;算法。它就像是一位经验丰富的 “老江湖”&#xff0c;以其简单而又强大的方式&#xff0c;在众多机器学习任务中占据着不可或缺的地位。 K - 近邻…

算法分析—— 《归并排序》

《排序数组》 题目描述&#xff1a; 给你一个整数数组 nums&#xff0c;请你将该数组升序排列。 你必须在 不使用任何内置函数 的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))&#xff0c;并且空间复杂度尽可能小。 示例 1&#xff1a; 输入&#xff1a;nums [5,2…

linux云服务器部署deepseek,并通过网页访问

参考视频&#xff1a;https://www.douyin.com/root/search/linux%E5%AE%89%E8%A3%85%20deepseek?aid3aa2527c-e4f2-4059-b724-ab81a140fa8b&modal_id7468518885570940214&typegeneral 修改ollama配置文件 vim /etc/systemd/system/ollama.service 我的电脑硬盘只有4…

FastAdmin后端列表导入表格数据

后台添加数据的时候增加通过表格导入功能 如下图index.html页面增加导入和模板下载按钮代码如下 <div class"panel panel-default panel-intro">{:build_heading()}<div class"panel-body"><div id"myTabContent" class"ta…

可调节图片参数,解决图片模糊及尺寸过小问题的工具

软件介绍 你是否正为图片模糊、尺寸太小而烦恼&#xff1f;别担心&#xff0c;有这样一款神器能帮你轻松解决。它能精准调节图片参数&#xff0c;即便原本模糊不清的图片&#xff0c;经它处理后也能变得高清锐利&#xff0c;瞬间让图片焕然一新。而且&#xff0c;它还具备导出…

Windows网络安全基础

随着互联网的发展和普及&#xff0c;Windows网络安全问题愈发严重。在本文中&#xff0c;我们将会介绍Windows网络安全的基本概念&#xff0c;包括网络攻击类型、网络安全威胁、网络安全防御措施等等&#xff0c;帮助初学者更好地了解Windows网络安全。 一、网络攻击类型 网络…

代码补全『三重奏』:EverEdit如何用上下文识别+语法感知+智能片段重构你的编码效率!

1 代码自动完成 1.1 应用场景 在编辑文档时&#xff0c;为了提高编辑效率&#xff0c;编辑器一般都会带有自动完成功能&#xff0c;比如&#xff1a;输入括号时自动补全另一半&#xff0c;输入文字时&#xff0c;自动补全剩下的部分。 1.2 使用方法 1.2.1 自动缩进 单击主菜…