网站首页 > 技术文章 正文
ECMAScript 6 新增了 Proxy 与 Reflect,让JavaScript也可以实现元编程。 Proxy 代理类通过包含捕获器的 ProxyHandler 对象来拦截目标对象的基本操作和方法,来改变目标对象原本的逻辑。而 Reflect 通过其 API 可以操作JavaScript对象。
创建代理
Proxy 类提供了构造器来创建 Proxy 对象。
Proxy(target, handler)
其中的 target 参数为要拦截的目标对象,handler 参数为处理程序对象。当缺少任何一个参数都会抛出 TypeError。
let target = {
name: "小王",
age: 20,
id: 2021
};
let proxy = new Proxy(target, {
get: function() {
return "ProxyHandler.get()";
}
});
proxy.name // ProxyHandler.get()
proxy.id // ProxyHandler.get()
由上可知,handler 对象中定义了 get 捕获器来对目标对象中的读取操作进行捕获拦截,从而修改读取操作的行为。只有发生在代理对象上,才会触发捕获器,在目标对象上执行仍会产生正常行为。
如想要创建一个空代理,可以在创建 Proxy 对象时, handler 参数传入的值为 {}即可。
let target = {};
let proxy = new Proxy(target, {});
捕获器
在 handler 中可以包含零个或多个捕获器,可直接或间接在代理对象上调用。handler 参数的类型为 ProxyHandler 类,除了上面介绍的 get() 方法外,还包含了其他的捕获器,如 getPrototypeOf()、has()、 apply() 等 13 个捕获器。
has
has 捕获器会对 in 运算符劫持,用于判断某个属性是否存在。
let target = {
name: "小王",
age: 20,
id: 2021
};
let proxy = new Proxy(target, {
has(target, p) {
if (p.startsWith('a'))
return false;
return p in target;
}
});
'name' in proxy // true
'age' in proxy // false
'id' in proxy // true
捕获器参数
在使用捕获器时,可以访问相应的参数来重建被捕获方法的原始行为。如上面的例子中的 has()捕获器会接收到目标对象、要查询的属性。可以通过调用封装了原始行为的 Reflect对象上的方法来重建,几乎 ProxyHandler 中的捕获器在 Reflect中,都能找到与之相对应的方法 。
let proxy = new Proxy(target, {
has(target, p) {
if (p.startsWith('a'))
return false;
return Reflect.has(...arguments);
}
});
当然,如果创建的代理对象中,不对目标对象进行修改,但需要捕获所有方法,可以直接传 Reflect。
let target = {
name: "小王",
age: 20,
id: 2021
};
let proxy = new Proxy(target, Reflect);
'age' in target // true
'age' in proxy // true
可撤销代理
使用 new Proxy() 来创建的代理,与目标对象之间的代理关系,会在其生命周期内一直存在。但有时也需要撤销代理对象与目标对象之间的联系,这时就可以使用 Proxy 提供的 revocable() 方法来创建一个支持可撤销的代理对象。
let target = {
name: "小王"
};
const { proxy, revoke } = Proxy.revocable(target, {
get() {
return "小信";
}
});
console.log(target.name); // 小王
console.log(proxy.name); // 小信
revoke();
console.log(target.name); // 小王
console.log(proxy.name); // TypeError: Cannot perform 'get' on a proxy that has been revoked
这里调用 revoke() 函数是幂等的,指的是无论调用多少次,其结果不变。当撤销后,在调用代理时,会抛出 TypeError 异常,如上所示。
反射 API
上面也介绍了 ProxyHandler 提供了 13 个方法来捕获目标对象中的操作,但若需要在捕获器中调用对象的默认行为,就可以使用 Reflect 提供的 API,其提供的 API 基本都有捕获器相对应。下面就介绍 apply 和 defineProperty 两个方法。
apply
Reflect 中提供了 apply 方法通过指定的参数列表发起目标函数的调用,和 Function.prototype.apply() 功能类似。
Reflect.apply(Math.min, Math, args); => Math.min.apply(Math, args);
当 Proxy 对象中的 ProxyHandler 使用 apply 捕获器来拦截函数调用时,可以使用 Reflect.apply() 来调用目标对象中函数的默认行为。
let target = function (a, b) {
return a + b;
}
let proxy = new Proxy(target, {
apply(target, thisArg, argArray) {
return Reflect.apply(...arguments) * 2;
}
});
target(1, 2); // 3
proxy.apply(null, [1, 2]); // 6
defineProperty
Reflect 提供的 defineProperty 方法与 Object.defineProperty() 基本相同,不同的是 Reflect 的方法返回的是 Boolean 值,该方法主要用于添加或修改对象上的属性。
let target = {
name: "小王",
age: 20,
id: 2021
};
target.address; // undefined
Reflect.defineProperty(target, "address", {value: "新疆"});
target.address; // 新疆
总结
ECMAScript 6 新增的 Proxy 可以对对象提供了基本操作的拦截和改变原本的逻辑,从而丰富了开发的灵活性。而 Reflect 提供的 API 可以对绝大部分对象进行操作,如 Reflect.getPrototypeOf() 类似于 Object.getPrototypeOf()。应用场景也非常大,如跟踪属性访问、隐藏属性、参数验证、数据绑定以及观察对象等。
猜你喜欢
- 2024-11-21 浅析GIF 格式图片的存储与解析
- 2024-11-21 如何用2 KB代码实现3D赛车游戏?2kPlus Jam大赛了解一下
- 2024-11-21 「实战」蘑菇街 PC 端首页,瀑布流布局的实现原理与细节技巧
- 2024-11-21 Knative 驾驭篇:带你 '纵横驰骋' Knative 自动扩缩容实现
- 2024-11-21 ECMAScript 6使用教程总结
- 2024-11-21 一道二进制子串算法,让面试官都解不出来?
- 2024-11-21 高级前端进阶,为什么要使用call、apply、bind?
- 2024-11-21 碎片时间学编程「202]:分组数组元素
- 2024-11-21 从入门到入土:Lambda完整学习指南,包教包会(上)
- 2024-11-21 碎片时间学编程「58]:取消组合数组元素
- 标签列表
-
- content-disposition (47)
- nth-child (56)
- math.pow (44)
- 原型和原型链 (63)
- canvas mdn (36)
- css @media (49)
- promise mdn (39)
- readasdataurl (52)
- if-modified-since (49)
- css ::after (50)
- border-image-slice (40)
- flex mdn (37)
- .join (41)
- function.apply (60)
- input type number (64)
- weakmap (62)
- js arguments (45)
- js delete方法 (61)
- blob type (44)
- math.max.apply (51)
- js (44)
- firefox 3 (47)
- cssbox-sizing (52)
- js删除 (49)
- js for continue (56)
- 最新留言
-