关于javascript:JavaScript-设计模式及代码实现代理模式

6次阅读

共计 2886 个字符,预计需要花费 8 分钟才能阅读完成。

代理模式

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

小结

  1. 依据繁多职责准则:就一个类(通常也包含对象和函数)而言,应该只有一个职责。
  2. 咱们利用代理模式让代理对象承当额定性能,不毁坏指标对象,从而不至于让指标对象变得臃肿而升高复用性和可维护性

3 JavaScript Proxy

JS 提供了 Proxy 类,能够十分不便地创立代理对象,从而实现基本操作的拦挡和自定义(如属性查找、赋值、枚举、函数调用等)。

Proxy 的用法非常简单:

const proxy = new Proxy(target, handler)
// target
// 要应用 Proxy 包装的指标对象(能够是任何类型的对象,包含原生数组,函数,甚至另一个代理)。// handler
// 一个通常以函数作为属性的对象,各属性中的函数别离定义了在执行各种操作时代理 p 的行为。

详见 MDN 文档 https://developer.mozilla.org…

3.1 Proxy 实现缓存代理

handler 对象有很多可选办法,其中 apply 办法用来拦挡函数调用操作

apply 办法承受 3 个参数,详见 https://developer.mozilla.org…

// 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…
  • set https://developer.mozilla.org…
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

完结,撒花🎉

正文完
 0