Proxy

要创建 Proxy(代理)对象,我们使用 Proxy 构造函数 new Proxy();。Proxy 构造函数接收两个项目:

  • 它将要代理的对象
  • 包含将为被代理对象处理的方法列表的对象

第二个对象叫做处理器.

Proxy 内的一个传递

创建 Proxy 的最简单方式是提供对象和空的 handler(处理器)对象。

var richard = {status: 'looking for work'};
var agent = new Proxy(richard, {});

agent.status; // 返回 'looking for work'

上述代码并没有对 Proxy 执行任何特殊操作,只是将请求直接传递给源对象!如果我们希望 Proxy 对象截获请求,这就是 handler 对象的作用了!

让 Proxy 变得有用的关键是当做第二个对象传递给 Proxy 构造函数的 handler 对象。handler 对象由将用于访问属性的方法构成。我们看看 get

Get Trap(捕获器)

get 用来截获对属性的调用:

const richard = {status: 'looking for work'};
const handler = {
    get(target, propName) {
        console.log(target); // `richard` 对象,不是 `handler` 也不是 `agent`
        console.log(propName); // 代理(本例中为`agent`)正在检查的属性名称
    }
};
const agent = new Proxy(richard, handler);
agent.status; // 注销 richard 对象(不是代理对象!)和正在访问的属性的名称(`status`)

在上述代码中,handler 对象具有一个 get 方法(因为被用在 Proxy 中,所以将"function"(方法)称之为"trap"(捕获器))。当代码 agent.status; 在最后一行运行时,因为存在 get 捕获器,它将截获该调用以获得 status(状态)属性并运行 get 捕获器方法。这样将会输出 Proxy 的目标对象(richard 对象),然后输出被请求的属性(status 属性)的名称。它的作用就是这些!它不会实际地输出属性!这很重要 —— 如果使用了捕获器,你需要确保为该捕获器提供所有的功能

从 Proxy 内部访问目标对象

如果我们想真正地提供真实的结果,我们需要返回目标对象的属性:

const richard = {status: 'looking for work'};
const handler = {
    get(target, propName) {
        console.log(target);
        console.log(propName);
        return target[propName];
    }
};
const agent = new Proxy(richard, handler);
agent.status; //  (1)打印 richard 对象,(2)打印被访问的​​属性,(3)返回 richard.status 中的文本

注意我们在 get trap 中添加了最后一行 return target[propName];,这样将会访问目标对象的属性并返回它。

直接获取 Proxy 的返回信息

此外,我们可以使用 Proxy 提供直接的反馈:

const richard = {status: 'looking for work'};
const handler = {
    get(target, propName) {
        return `He's following many leads, so you should offer a contract as soon as possible!`;
    }
};
const agent = new Proxy(richard, handler);
agent.status; // 返回文本 `He's following many leads, so you should offer a contract as soon as possible!`

对于上述代码,Proxy 甚至不会检查目标对象,直接对调用代码做出响应。

因此每当 Proxy 上的属性被访问,get trap 将接管任务。如果我们想截获调用以更改属性,则需要使用 set trap!

set trap 用来截获将更改属性的代码。set trap 将接收: 它代理的对象 被设置的属性 Proxy 的新值

const richard = {status: 'looking for work'};
const handler = {
    set(target, propName, value) {
        if (propName === 'payRate') { // 如果工资正在确定,则需要15%作为佣金。
            value = value * 0.85;
        }
        target[propName] = value;
    }
};
const agent = new Proxy(richard, handler);
agent.payRate = 1000; // 将演员的工资设置为 1,000美元
agent.payRate; // 850美元是演员的实际工资

在上述代码中,注意 set trap 会检查是否设置了 payRate 属性。如果设置了,Proxy 就从中拿走 15% 的费用作为自己的佣金!当演员的薪酬是一千美元时,因为 payRate 属性已设置,代码从中扣除 15% 的费用,并将实际 payRate 属性设为 850

其他 Trap

我们查看了 get 和 set trap(可能是你最常用到的 Trap),但是实际上总共有 13 种不同的 Trap,它们都可以用在处理程序中!

  1. get trap - 使 proxy 能处理对属性访问权的调用
  2. set trap - 使 proxy 能将属性设为新值
  3. apply trap - 使 proxy 能被调用(被代理的对象是函数)
  4. has trap - 使 proxy 能使用 in 运算符
  5. deleteProperty trap - 使 proxy 能确定属性是否被删除
  6. ownKeys trap - 使 proxy 能处理当所有键被请求时的情况
  7. construct trap - 使 proxy 能处理 proxy 与 new 关键字一起使用当做构造函数的情形
  8. defineProperty trap - 使 proxy 能处理当 defineProperty 被用于创建新的对象属性的情形
  9. getOwnPropertyDescriptor trap - 使 proxy 能获得属性的描述符
  10. preventExtenions trap - 使 proxy 能对 proxy 对象调用 Object.preventExtensions()
  11. isExtensible trap - 使 proxy 能对 proxy 对象调用 Object.isExtensible
  12. getPrototypeOf trap - 使 proxy 能对 proxy 对象调用 Object.getPrototypeOf
  13. setPrototypeOf trap - 使 proxy 能对 proxy 对象调用 Object.setPrototypeOf

可以看出,有很多 trap 可以让 proxy 管理如何处理被代理的对象的调用。

 

 

一开始,可能不太清楚的是,ES5 中已经提供了 getter 和 setter 方法,为何还要 Proxy。对于 ES5 的 getter 和 setter 方法,你需要提前知道要获取/设置的属性:

var obj = {
    _age: 5,
    _height: 4,
    get age() {
        console.log(`getting the "age" property`);
        console.log(this._age);
    },
    get height() {
        console.log(`getting the "height" property`);
        console.log(this._height);
    }
};

对于上述代码,注意在初始化对象时,我们需要设置 get age() 和 get height()。因此,当我们调用下面的代码时,将获得以下结果:

obj.age; // 打印 'getting the "age" property' 和 5
obj.height; // 打印 'getting the "height" property' 和 4

但是当我们向该对象添加新的属性时,看看会发生什么:

obj.weight = 120; // 在对象上设置一个新的属性
obj.weight; // 只打印120

注意,并没有显示 age 和 height 属性那样生成的 getting the "weight" property 消息。

对于 ES6 中的 Proxy,我们不需要提前知道这些属性*:

const proxyObj = new Proxy({age: 5, height: 4}, {
    get(targetObj, property) {
        console.log(`getting the ${property} property`);
        console.log(targetObj[property]);
    }
});

proxyObj.age; // 打印 'getting the age property' 和 5
proxyObj.height; // 打印 'getting the height property' 和 4

就像 ES5 代码那样一切正常,但是当我们添加新的属性时,看看会发生什么:

proxyObj.weight = 120; // 在对象上设置一个新的属性
proxyObj.weight; // 打印 'getting the weight property' 和 120

看到了吗?向 proxy 对象中添加了 weight 属性,稍后检索它时,它显示了一条日志消息!

因此 proxy 对象的某些功能可能看起来类似于现有的 ES5 getter/setter 方法,但是对于 proxy,在初始化对象时,不需要针对每个属性使用 getter/setter 初始化对象。

 

Proxy 对象介于真正的对象和调用代码之间。调用代码与 Proxy 交互,而不是真正的对象。要创建 Proxy:

  • 使用 new Proxy() 构造函数
    • 将被代理的对象传入为第一项
    • 第二个对象是 handler(处理器)对象
  • handler 对象由 13 种不同的 trap 之一构成
  • trap 是一种函数,将截获对属性的调用,让你运行代码
  • 如果未定义 trap,默认行为会被发送给目标对象

Proxy 是一种强大的创建和管理对象之间的交互的新方式。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值