Proxy 代理
1. Proxy 代理创建
Proxy
对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义
ES6
原生提供 Proxy
构造函数,用来生成 Proxy
实例,Proxy
对象需要传入两个参数
target
—— 被代理的目标对象,可以是任何东西,包括函数handler
—— 代理配置,带有捕捉器即拦截操作的方法的对象,内部可以定义零个或多个代理函数
let proxy = new Proxy(target, handler)
- 如果
handler
没有设置拦截,所有对proxy
的操作都直接转发给了target
目标对象
// 创建代理
let obj = {
};
let proxy = new Proxy(obj, {
});
// 没有设置捕捉器则会将proxy操作直接转发给target
proxy.name = 'jsx';
console.log(proxy); // Proxy {name: 'jsx'}
2. Proxy 拦截器
- 代理对象必须与被代理的对象具有相同特性,被代理对象不能改的,代理对象同样不能改
- 代理对象的方法的返回值类型必须与被代理对象一致
Proxy
支持的 13 种拦截方法:
拦截器 | 描述 |
---|---|
get(target, propKey, receiver) | 拦截对象属性的读取 |
set(target, propKey, value, receiver) | 拦截对象属性的设置,返回一个布尔值 |
has(target, propKey) | in 操作符的捕捉器,返回一个布尔值 |
deleteProperty(target, propKey) | delete 操作符的捕捉器,返回一个布尔值 |
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, newTarget) | 拦截 Proxy 实例作为构造函数调用的操作,new proxy(...args) |
2-1 get()
get(target, property, receiver)
该方法用于拦截某个属性的读取操作
target
—— 是目标对象,该对象被作为第一个参数传递给new Proxy
property
—— 目标属性名receiver
—— 如果目标属性是一个getter
访问器属性,则receiver
就是本次读取属性所在的this
对象,通常是proxy
实例本身
// get(target, property, receiver) 读取属性
let obj = {
name: 'jsx'
}
let proxy = new Proxy(obj, {
get(target, property, receiver) {
if (property in target) return target[property];
}
})
console.log(proxy.name); // jsx
如果一个属性不可配置且不可写,则 Proxy
不能修改该属性,否则通过 Proxy
对象访问该属性会报错
// 当一个对象不可读不可写 proxy则不能修改属性
let obj1 = Object.defineProperties({
}, {
name: {
value: 'ljj',
writable: false,
configurable: false
}
})
let proxy1 = new Proxy(obj1, {
get(target, property, receiver) {
// 原对象不可写不可配置,代理对象proxy则不可修改
// return 'ljj'
return target[property]
}
});
console.log(proxy1.name); // ljj
2-2 set()
set(target, property, value, receiver)
该方法用来拦截对象属性的设置
target
—— 是目标对象,该对象被作为第一个参数传递给new Proxy
property
—— 目标属性名称value
—— 目标属性的值receiver
—— 与get
捕捉器类似,仅与setter
访问器属性相关,通常是proxy
实例本身
set
拦截无论成功或者失败应当返回一个布尔值,在严格模式中,set
拦截器如果没有返回true
,就会报错,set
代理返回false
或者undefined
,都会报错
let obj = {
name: 'jsx'
};
let proxy = new Proxy(obj, {
set(target, property, value, receiver) {
if (typeof value == 'string') {
// 属性值必须是字符串才能赋值
target[property] = value;
return true
} else {
return false
}
}
})
proxy.name = 'ljj';
console.log(proxy.name); // 设置false后不会报错 无改变
如果目标对象自身的某个属性不可写,那么 set
方法将不会生效
对二级或以上的属性修改并不会触发 set
拦截器
// 对象属性不可写。set拦截器不会生效
let obj1 = Object.defineProperty({
}, 'name', {
value: 'jsx',
writable: false
})
let proxy1 = new Proxy(obj1, {
set(target, property, value, receiver) {
if (typeof value == 'string') {
target[property] = value;
return true
} else {
return false
}
}
})
proxy1.name =