JavaScript 设计模式及代码实现——代理模式
代理模式
1 定义
为其他对象提供一种代理以控制对这个对象的访问
在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

2 应用举例
2.1 缓存代理
现在我们有一个可以查询城市经纬度的函数:
const getLatLng = (address) => {
if (address === "Beijing") {
return "北京经纬度";
} else if (address === "Hangzhou") {
return "杭州经纬度";
} else if (address === "Shanghai") {
return "上海经纬度";
} else if (address === "Nanjing") {
return "南京经纬度";
} else {
return "";
}
};
如果我们多次查询南京的经纬度,每次都要经过 4 次判断,我们通过 getLatLngProxy 函数将查询结果缓存下来,从而避免多次重复判断
const getLatLngProxy = ((fn) => {
const geoCache = {};
return (address) => {
console.log("缓存=" + geoCache[address]);
return (geoCache[address] ??= fn(address));
};
})(getLatLng);
getLatLngProxy("Nanjing"); // 缓存=undefined
getLatLngProxy("Nanjing"); // 缓存=南京经纬度
4 次判断看不出什么,但是如果 getLatLng 中的操作不是判断,而是需要很复杂的计算,需要消耗很长时间,这时缓存的优势就很明显了
我们在不修改原函数的前提下,通过高阶函数创建了一个拥有缓存效果的代理函数
2.2 Vue2 响应式原理——数据代理
如果你学习过 Vue2 响应式原理,一定知道其中重要的一环:数据代理。不知道也没关系,下面举个简单的栗子来说明一下。
const obj = {
name: "JiMing",
};
let name = obj.name; // 访问 obj.name
obj.name = "Ji"; // 修改 obj.name
假设现在有一个对象 obj,如果我想在访问或修改obj.name时做一些额外的操作,比如打印信息到控制台,该如何实现?
JS 提供了 **Object.defineProperty()**方法,该方法可以在一个对象上定义一个新属性,或者修改一个对象的现有属性,并返回此对象。
我们可以利用这个 API 在代理对象上添加目标对象的同名属性,同时添加额外的操作
const proxyObj = {}; // 代理对象
Object.defineProperty(proxyObj, "name", {
get() {
console.log("访问了 obj.name");
return obj.name;
},
set(val) {
console.log("修改了 obj.name");
obj.name = val;
},
});
现在我们只要访问或修改代理对象的 name 属性,就可以实现访问或修改obj.name,同时打印信息到控制台
Vue2 就是通过此方法将 data 中的属性添加到 vm 实例上,因此我们可以使用this.属性名来访问属性,并且和我们打印信息到控制台一样,Vue 也添加了额外的操作比如通过 set 实现数据监听,从而完成响应式变化
小结
- 根据单一职责原则:就一个类(通常也包括对象和函数)而言,应该只有一个职责。
- 我们利用代理模式让代理对象承担额外功能,不破坏目标对象,从而不至于让目标对象变得臃肿而降低复用性和可维护性
3 JavaScript Proxy
JS 提供了 Proxy 类,可以非常方便地创建代理对象,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。
Proxy 的用法非常简单:
const proxy = new Proxy(target, handler)
// target
// 要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
// handler
// 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
详见 MDN 文档 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy
3.1 Proxy 实现缓存代理
handler 对象有很多可选方法,其中 apply 方法用来拦截函数调用操作
apply 方法接受 3 个参数,详见https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/apply
// apply 的 3 个参数
// target 目标对象
// thisArg 被调用时的上下文对象
// argArray 被调用时的参数数组。
const geoCache = {};
const getLatLngProxy = new Proxy(getLatLng, {
apply(target, thisArg, argArray) {
const address = argArray[0];
console.log("缓存=" + geoCache[address]);
return (geoCache[address] ??= target(address));
},
});
getLatLngProxy("Hangzhou"); // 缓存=undefined
getLatLngProxy("Hangzhou"); // 缓存=杭州经纬度
我们调用代理函数 getLatLngProxy 时会触发 apply 方法
注意这里我们的目标对象是 getLatLng 函数,即 apply 的 target 就是 getLatLng 的引用,因此我们调用 target 就相当于调用 getLatLng
3.2 Vue3 的数据代理
Vue2 使用 Object.defineProperty 来实现数据代理,但是这个方法存在局限性,比如:普通属性我们可以通过 set 方法获取到其变化的信息,但是使用 push 方法改变数组,无法通过 set 获取到。
因此 Vue3 改用 Proxy 来实现数据代理
和 apply 方法类似,handler 中还有 get 和 set 方法用来拦截对属性访问、修改的操作
详见
- get https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/get
- set https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy/Proxy/set
const obj = {
name: "JiMing",
};
const proxyObj = new Proxy(obj, {
// target 目标对象 即 obj
// property 被获取的属性名。
get(target, property) {
console.log(`访问了 obj.${property}`);
return target[property];
},
// target 目标对象 即 obj
// 将被设置的属性名
set(target, property, value) {
console.log(`修改了 obj.${property}`);
target[property] = value;
},
});
proxyObj.name; // 访问了 obj.name
proxyObj.name = "Ji"; // 修改了 obj.name
完结,撒花
JavaScript 设计模式及代码实现——代理模式的更多相关文章
- javaScript 设计模式系列之三:代理模式
介绍 代理模式为其他对象提供一种代理以控制对这个对象的访问. 根据代理模式的使用目的不同,代理模式又可以分为多种类型: 远程代理(Remote Proxy) 虚拟代理(Virtual Proxy)如需 ...
- javascript设计模式详解之策略模式
接上篇命令模式来继续看下js设计模式中另一种常用的模式,策略模式.策略模式也是js开发中常用的一种实例,不要被这么略显深邃的名字给迷惑了.接下来我们慢慢看一下. 一.基本概念与使用场景: 基本概念:定 ...
- 【转】设计模式(十一)代理模式Proxy(结构型)
设计模式(十一)代理模式Proxy(结构型) 1.概述 因为某个对象消耗太多资源,而且你的代码并不是每个逻辑路径都需要此对象, 你曾有过延迟创建对象的想法吗 ( if和else就是不同的两条逻辑路径) ...
- javascript设计模式详解之命令模式
每种设计模式的出现都是为了弥补语言在某方面的不足,解决特定环境下的问题.思想是相通的.只不过不同的设计语言有其特定的实现.对javascript这种动态语言来说,弱类型的特性,与生俱来的多态性,导致某 ...
- 设计模式之jdk动态代理模式、责任链模式-java实现
设计模式之JDK动态代理模式.责任链模式 需求场景 当我们的代码中的类随着业务量的增大而不断增大仿佛没有尽头时,我们可以考虑使用动态代理设计模式,代理类的代码量被固定下来,不会随着业务量的增大而增大. ...
- Java设计模式(10)代理模式(Proxy模式)
理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,P ...
- JS设计模式(3)代理模式
什么是代理模式? 情景:小明追女生 A 非代理模式:小明 =花=> 女生A 代理模式:小明 =花=> 让女生A的好友B帮忙 =花=> 女生A 定义:为其他对象提供一种代理以控制对这个 ...
- Javascript设计模式之我见:迭代器模式
大家好!本文介绍迭代器模式及其在Javascript中的应用. 模式介绍 定义 提供一种方法顺序一个聚合对象中各个元素,而又不暴露该对象内部表示. 类图及说明 Iterator抽象迭代器 抽象迭代器负 ...
- 【读书笔记】读《JavaScript设计模式》之装饰者模式
一.定义 装饰者模式可用来透明地把对象包装在具有同样接口的另一个对象之中.这样一来,你可以给一个方法添加一些行为,然后将方法调用传递给原始对象.相对于创建子类来说,使用装饰者对象是一种更灵活的选择(装 ...
随机推荐
- 02 RESTFul接口和HTTP的幂等性分析
RESTFul接口和HTTP的幂等性分析 REST全称是Representational State Transfer,中文为表述性状态转移,REST指的是一组架构约束条件和原则 RESTful表述的 ...
- CAP:多重注意力机制,有趣的细粒度分类方案 | AAAI 2021
论文提出细粒度分类解决方案CAP,通过上下文感知的注意力机制来帮助模型发现细微的特征变化.除了像素级别的注意力机制,还有区域级别的注意力机制以及局部特征编码方法,与以往的视觉方案很不同,值得一看 来源 ...
- MES 系统介绍
MES系统是一套面向制造企业车间执行层的生产信息化管理系统.MES可以为企业提供包括制造数据管理.计划排程管理.生产调度管理.库存管理.质量管理.人力资源管理.工作中心/设备管理.工具工装管理.采购管 ...
- 一条 SQL 语句是如何执行的
一条 SQL 语句是如何执行的 SQL查询语句 select * from user where ID=10; MySQL 的基本架构可以分为 Server 层和存储引擎两部分.Server 层又包含 ...
- 合宙Air32F103CBT6开发板上手报告
2022年6月初合宙新上市了 Air32F103 系列 MCU, 市面上 STM32F103 的克隆军队又增加了新的一员. 这次不知道是哪家的贴牌, 分 Air32F103CBT6 和 Air32F1 ...
- UiPath循环活动Do While的介绍和使用
一.Do While的介绍 先执行循环体, 再判断条件是否满足, 如果满足, 则再次执行循环体, 直到判断条件不满足, 则跳出循环 二.Do While在UiPath中的使用 1. 打开设计器,在设计 ...
- Qt+ECharts开发笔记(二):Qt窗口动态调整大小,使ECharts跟随Qt窗口大小变换而变换大小
前言 上一篇将ECharts嵌入Qt中,在开始ECharts使用之前,还有一个很重要的功能,就是在窗口变换大小的时候,ECharts的图表尺寸也要跟随Qt窗口变换大小而变换大小. Demo演示 ...
- docker for window WSL 2 installation is incomplete 错误,导致docker无法启动
1.错误截图如下: 2.错误原因:由于wsl2版本旧,根据提示让我们手动更新包,去微软官网下载最新wsl2后,安装完成重启即可解决. 3.下载地址:download地址
- html和css的常用语法代码详解
前端html html 超文本标记语言.文本,图片,视频,音频. 网页基本信息 一个基础的网页具有的一些信息. <!-- 这是注释--> <!--!DOCTYPE网页约束规范--&g ...
- 温控器/胎压检测/电表/热泵显示控制器等,低功耗高抗干扰断/段码(字段式)LCD液晶显示驱动IC-VK2C22A/B,替代市面16C22,44*4/40*4点显示
产品品牌:永嘉微电/VINKA 产品型号:VK2C22A/B 封装形式:LQFP52/48 产品年份:新年份 概述: VK2C22是一个点阵式存储映射的LCD驱动器,可支持最大176点(44SEGx4 ...