通俗易懂的JS之Proxy

  • A+
所属分类:Web前端
摘要

与掘金文章同步,地址:https://juejin.cn/post/6964398933229436935引入一个现实生活中的案例

与掘金文章同步,地址:https://juejin.cn/post/6964398933229436935

什么是代理模式

引入一个现实生活中的案例

我们作为用户需要去如何评估一个房子的好坏、如何办理住房手续等一些列繁琐的事物吗?显然,用户肯定不愿意这样做。用户最关心的是结果,用户对房子提出需求以及提供对等价值的金钱就可以获得满意的房子,这就是结果。

那么谁为用户去解决一系列繁琐的买房过程呢?当然就是“房屋中介”了!房屋中介的作用就是在房地产开发经营与消费的供求市场中,为交易物体提供评估、交易、代理、咨询等服务及善后服务的机构。

结合案例理解代理模式的定义

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

结合上边的现实生活中的案例来理解这一段话,代理模式就是为其他用户提供一种代理,不让用户知道买房的过程,而用户应该关心的是如何获得满意的结果。代理所需要做的事情就是完成这一买房的过程

什么是Proxy

Proxy用于修改某些操作的默认行为,在目标对象前架设一个“拦截”层,外界对该对象的访问都必须先通过这一层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。——《ES6标准入门》- 第12章

Proxy支持的拦截操作有get(target, propKey, receiver)和set(target, propKey, value, receiver),对于可以设置但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。

  1. get实例方法:拦截对象属性的读取;
  2. set实例方法:拦截对象属性的设置。

get(target, propKey, receiver)

定义一个person对象

var person = {     name: "kongsam",     age: 21,     hobbies: [         "看动漫",         "骑行",         "玩游戏"     ] } 

现在实例化一个Proxy对象,来拦截外界对person的操作

var proxy = new Proxy(person, {     get: function (target, property) {           // 打印target和property,查看里面到底是什么。           console.log("target = ", target);           console.log("property = ", property);           // 判断外界访问的对象属性是否存在于目标对象中。           if (property in target) {                 return target[property];           } else {                 // 如果外界访问的对象属性不存在与目标对象中,抛出异常。                 throw new ReferenceError('Property "' + property + '" 不存在。');           }     }, }); 

当我proxy.name时,由于person对象已经被该代理对象给代理了,并且该代理对象为person对象设置了一层拦截,就是所谓的拦截层,所以每当我通过该代理对象访问person中存在的属性时,都会调用get函数,因为get函数是拦截对象属性的读取的。

get: function (target, property) 中的两个参数target和property分别接收到的信息如图所示

通俗易懂的JS之Proxy

上面说到,通过该代理对象访问person中存在的属性时,如果访问不存在的属性,会发生什么?

通俗易懂的JS之Proxy

我又是做了什么样的工作,才导致访问不存在的属性时,它会抛出异常呢?

因为外界对该对象的访问都必须先通过一层拦截,而拦截的过程中提供了一种机制可以对外界的访问进行过滤和改写。

// 判断外界访问的对象属性是否存在于目标对象中。 if (property in target) {     return target[property]; } else {     // 如果外界访问的对象属性不存在与目标对象中,抛出异常。     throw new ReferenceError('Property "' + property + '" 不存在。'); } 

这里的if语句判断实际上就是我设置的一个机制,即对外界的访问进行过滤和改写。如果没有这种机制,访问不存在的属性只会返回undefined。

通俗易懂的JS之Proxy

set(target, propKey, value, receiver)

有get就有对应的set,依旧是上面的person对象,这时我有个新需求,即修改age属性时,值不能超过150且是整数。

新增proxy对象中的set方法

var proxy = new Proxy(person, {     set: function (target, property, value) {           // 打印target、property和value,查看里面到底是什么。           console.log("target = ", target);           console.log("property = ", property);           console.log("value = ", value);           if (property === "age") {                 if (!Number.isInteger(value)) {                   throw new TypeError("age的值不是整数!");                 }                 if (value > 150) {                   throw new RangeError("age的值不能大于150!");                 }           }     }, }); 

当我执行proxy.age = 100时,set的三个参数分别接收到的信息如下图所示

通俗易懂的JS之Proxy

set方法的作用是用于拦截某个属性的赋值操作,那么我如果对age的赋值操作不满足我刚刚提出的新需求,会发生什么?

通俗易懂的JS之Proxy

很明显,会抛出异常。

总结

Proxy 就是一个拦截层,你给出要代理的对象,外界访问这个对象不是直接访问他的,而是访问这个Proxy的实例对象的。我们可以通过Proxy为外界访问进行过滤和改写,如赋值时需满足某些条件。

代理对象中还有许多的方法,如has、deleteProperty、ownKeys、getOwnPropertyDescriptor、defineProperty等等。这些都是用于拦截的方法,针对于不同的情况而出现的。