Proxy代理器
基本用法
- ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例
var proxy = new Proxy(target, handler)
target
参数表示所要拦截的目标对象handler
参数也是一个对象,用来定制拦截行为new Proxy()
之后返回一个对象,可以通过该对象,对target
进行操作
个人理解:我们来做个类比,
A
相当于target
,B
相当于proxy
get(target, propKey, receiver)
- 作用:拦截对象属性读取。比如
proxy.foo 和 proxy['foo']
- 参数
target
:传进Proxy
构造函数的目标对象propKey
:读取某个属性时的属性名receiver
:执行Proxy
构造函数后,返回的实例
- 作用:拦截对象属性读取。比如
set(target, propKey, value, receiver)
- 作用:拦截对象属性的设置。比如
proxy.foo = v或proxy['foo'] = v
,返回一个布尔值 - 参数
value
:读书某个属性时的属性值
- 作用:拦截对象属性的设置。比如
has(target, propKey)
- 作用:拦截
propKey in proxy
的操作,返回一个布尔值
- 作用:拦截
deleteProperty(target, propKey)
- 作用:拦截
delete proxy[propKey]
的操作,返回一个布尔值
- 作用:拦截
ownKeys(target)
- 作用:拦截
Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
、for...in循环
,返回一个数组 - 该方法返回目标对象所有自身的属性的属性名,而
Object.keys()
的返回结果仅包括目标对象自身的可遍历属性
- 作用:拦截
getOwnPropertyDescriptor(target, propKey)
- 作用:拦截
Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象
- 作用:拦截
defineProperty(target, propKey, propDesc)
- 作用:拦截
Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值
- 作用:拦截
preventExtensions(target)
- 作用:拦截
Object.preventExtensions(proxy)
,返回一个布尔值
- 作用:拦截
getPrototypeOf(target)
- 作用:拦截
Object.getPrototypeOf(proxy)
,返回一个对象
- 作用:拦截
isExtensible(target)
- 作用:拦截
Object.isExtensible(proxy)
,返回一个布尔值
- 作用:拦截
setPrototypeOf(target, proto)
- 作用:拦截
Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。
- 作用:拦截
- 如果目标对象是函数,那么还有两种额外操作可以拦截
apply(target, object, args)
- 作用:
拦截 Proxy 实例
作为函数调用的操作,比如proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
- 作用:
construct(target, args)
- 作用:拦截 Proxy 实例作为构造函数调用的操作,比如
new proxy(...args)
```javascript // 案例一 const obj = { age: 18 }; const handler = { get: function (target, propKey, receiver) { return target[propKey] }, set: function (target, propKey, value, receiver) { // 可以在方法上做一些处理,比如阻止修改原型 if (propKey === ‘proto‘) { return false; } target[propKey] = value; return true; }, has: function (target, propKey) { return target[propKey] !== undefined }, deleteProperty: function (target, propKey) { if (propKey === ‘proto‘) { return false; } delete target[propKey]; return true; }, getOwnPropertyDescriptor: function (target, propKey) { return Object.getOwnPropertyDescriptor(target, propKey); }, defineProperty: function (target, propKey, propDesc) { // 外部调用Object.defineProperties(proxy, propDescs),会逐次的调用这个函数 Object.defineProperty(target, propKey, propDesc); return true; }, preventExtensions: function (target) { // 即使不传入target,也要调用Object.preventExtensions()。不然在外部调用,会报错 Object.preventExtensions(); return true }, getPropertypeOf: function (target) { return Object.getPrototypeOf(target); }, isExtensible: function (target) { return Object.isExtensible(target) }, setPropertypeOf: function (target, proto) { Object.setPrototypeOf(target, proto); return true } }
- 作用:拦截 Proxy 实例作为构造函数调用的操作,比如
const proxy = new Proxy(obj, handler);
<a name="wsUyk"></a>
## 回收Proxy代理
- `Proxy.revocable(target, handle)`
- 作用:返回一个可取消的 Proxy 对象,该对象的`proxy`属性是`Proxy实例`,`revoke`属性是一个函数,可以取消`Proxy`实例
- 当执行`revoke函数`之后,再访问`Proxy实例`,就会抛出一个错误
```javascript
let target = {};
let handler = {};
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
proxy.foo // 123
revoke();
proxy.foo // TypeError: Revoked
this问题
// 案例一:this隐式绑定
const target = {
m: function () {
console.log(this === proxy);
}
};
const handler = {};
const proxy = new Proxy(target, handler);
target.m() // false
proxy.m() // true
// 案例二:this由于隐式绑定,而产生的问题
let set = new WeakMap();
let target = {
get value() {
return set.get(this);
},
set value(val) {
set.set(this, val)
}
}
target.value = 456;
let proxy = new Proxy(target, {});
console.log(target.value); // 456
console.log(proxy.value); // undefined
// 案例三
// 有些原生对象的内部属性,只有通过正确的this才能拿到,所以 Proxy 也无法代理这些原生对象的属性
const target = new Date();
const handler = {};
const proxy = new Proxy(target, handler);
proxy.getDate(); // TypeError: this is not a Date object.
proxy 的应用
// 案例一:用proxy实现双向绑定
const data = { text: '' };
const input = document.querySelector('input');
const p = document.querySelector('p');
const handler = {
set(target, key, value) {
target[key] = value;
// 数据变化 ——> 视图变化
input.value = value;
p.innerText = value;
return true;
}
}
const proxy = new Proxy(data, handler);
input.onchange = function (e) {
// 视图变化 ——> 数据变化
proxy.text = e.target.value;
}