ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

JavaScript 设计模式及代码实现——代理模式

2022-09-01 19:34:56  阅读:410  来源: 互联网

标签:obj name 对象 代码 JavaScript Proxy address 设计模式 target


代理模式

1 定义

为其他对象提供一种代理以控制对这个对象的访问

在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

album_temp_1661991312

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 实现数据监听,从而完成响应式变化

小结

  1. 根据单一职责原则:就一个类(通常也包括对象和函数)而言,应该只有一个职责。
  2. 我们利用代理模式让代理对象承担额外功能,不破坏目标对象,从而不至于让目标对象变得臃肿而降低复用性和可维护性

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 方法用来拦截对属性访问、修改的操作

详见

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

完结,撒花

标签:obj,name,对象,代码,JavaScript,Proxy,address,设计模式,target
来源: https://www.cnblogs.com/bidong/p/16647574.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有