前端proxy代理对象
1.学习背景
学习vue3过程中,发现vue3相比于vue2的最大优势在于,尤雨溪大佬在vue3中使用proxy 代理对象,vue2中使用的双向绑定Object.defineProperty,这两者有什么区别
2.Object.defineProperty
使用方法:
// obj:绑定属性的目标对象
// property: 绑定的属性名
// descriptor: 属性描述(配置),且此参数本身为一个对象
Object.defineProperty(obj, property, descriptor)
- obj:绑定属性的目标对象
- property: 绑定的属性名
- descriptor: 属性描述(配置),且此参数本身为一个对象
- value: 设置属性默认值
- writable:设置属性是否能够修改
- enumerable: 设置属性是否可以枚举,即是否允许遍历
- configurable: 设置属性是否可以删除或编辑
- get: 获取属性的值
- set: 设置属性的值
示例:
//将对象obj 中的值传给target
let obj = {age: 18,arr: [1, 2, 3]
}
let target = {}for (let key in obj) {Object.defineProperty(target, key, {value: obj[key]})
}
console.log(target) // 这样我们就获得了obj中的值》》》》》》》》》》》》》》》》》》》》》》》》
{age: 18, arr: [1, 2, 3]}
2.1 通过writable设置是否可写/修改
使用writable参数判断对象的值是否能修改
let param = {};
Object.defineProperty(param,'nums',{value:18,writable:false});
console.log(param);
param.nums=20;
console.log(param);
》》》》》》》》》》》》》》》》》》》》》》》》
{nums: 18}
{nums: 18}
2.2 通过enumerable设置是否允许遍历
let param = {};
Object.defineProperty(param,'nums',{value:18,enumerable:false});
Object.defineProperty(param,'name',{value:999,enumerable:true});
for(let key in param){console.log(key)
}
》》》》》》》》》》》》》》》》》》》》》》》》
name //只打印name
2.3 通过configurable设置属性是否可删
let param = {};
Object.defineProperty(param,'nums',{value:18,configurable:false});
Object.defineProperty(param,'name',{value:999,configurable:true});
delete param.nums
delete param.name
console.log(param)
》》》》》》》》》》》》》》》》》》》》》》》》
{nums: 18} //name 被删掉了
2.4 通过get、set设置值
注意:在使用get和set方法时不能使用value和writable属性
get:
let param = {};
Object.defineProperty(param,'nums',{get(){return 20}});
console.log(param.nums)
》》》》》》》》》》》》》》》》》》》》》》》》
20 //输出set:
let param = {};
Object.defineProperty(param,'nums',{set(val){console.log(val)}});
param.nums=10
》》》》》》》》》》》》》》》》》》》》》》》
10 //输出
3.Proxy代理对象
// Proxy的基础用法如下,它需要传入两个参数
const p = new Proxy(target, handler)target:要使用 Proxy 包装的目标对象(可以是任何类型的对象,包括原生数组,函数,甚至另一个代理)。
handler: 一个通常以函数作为属性的对象,各属性中的函数分别定义了在执行各种操作时代理 p 的行为。
3.1定义一个代理对象
const obj = {age: 18
}
const objProxy = new Proxy(obj, {})
console.log(objProxy) // 我们这里打印代理对象
3.2 Proxy捕获器
在定义代理的过程中,handler 被称之为捕获器
下面为三个捕获器:
- get捕获器
- set捕获器
- defineProperty捕获器
get捕获器:
const obj = {age: 18
}
const objProxy = new Proxy(obj, {get(target, key) {console.log('获取对象' + key + '属性的值') // 这里我们可以捕捉到获取对象key的值到操作return target[key]}
})
console.log(obj.age) // 这里先输出 获取对象age属性的值, 再输出 18
get方法用于拦截对象的读取属性操作,它有三个参数,并且可以返回任何值
- target:目标对象
- property: 被读取的属性名
- receiver: Proxy 或者继承 Proxy 的对象
get方法可以拦截目标对象的以下操作
- 访问属性:如
obj.age
- 访问原型链上的属性:Object.create(obj)[age]
- 访问一个内置对象的属性:Reflect.get(arget, propertyKey[, receiver])
set捕获器:
const obj = {age: 18
}
const objProxy = new Proxy(obj, {set(target, key, val) {if(key === 'name' && typeof val === 'string') { // 可以做属性类型检查拦截等操作target[key] = val} else {throw new TypeError("该属性的值必须是String类型");}}
})
console.log(obj.age)
set方法是设置属性值操作的捕获器。它有四个参数,this会绑定到handler对象上,并且返回一个布尔值
- target: 目标对象
- property:将被设置的属性名或一个Symbol
- value:新属性值
- receiver: 最初被调用的对象。通常是 proxy 本身,但 handler 的 set 方法也有可能在原型链上,或以其他方式被间接地调用(因此不一定是 proxy 本身)。
注意:假设有一段代码执行
obj.name = "mr.z"
,obj
不是一个 proxy,且自身不含name
属性,但是它的原型链上有一个 proxy,那么,那个 proxy 的set()
处理器会被调用,而此时,obj
会作为 receiver 参数传进来。
set方法会拦截目标对象的以下操作:
- 给属性赋值:objProxy.name = ‘mr.z’
- 给继承到属性赋值:Object.create(objProxy)[name] = ‘mr.z’
- Reflect.set() // 这个不做过多讲解
对于以下情况,Proxy会抛出错误
- 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
- 如果目标属性没有配置存储方法,即
[[Set]]
属性的是undefined
,则不能设置它的值。 - 在严格模式下,如果
set()
方法返回false
,那么也会抛出一个TypeError
异常。
defineProperty 捕获器
let obj = new Proxy({}, {defineProperty(target, property, descriptor) {console.log('设置的属性: ' + prop);return true;}
});var desc = { configurable: true, enumerable: true, value: 'mr.z' };
Object.defineProperty(obj, 'name', desc); // "设置的属性: name"
defineProperty方法有三个参数,并且它的返回值必须是一个布尔值
- target:目标对象
- property:待检索其描述的属性名
- descriptor:待定义或修改的属性的描述符
在Proxy
中共有13
个捕获器,它们用于我们对对象、函数的方法调用监听。下面是Proxy
捕获器以及它们的触发条件。
对象中的方法 | 对应触发条件 |
---|---|
handler.getPrototypeOf() | Object.getPrototypeOf 方法的捕捉器 |
handler.setPrototypeOf() | Object.setPrototypeOf 方法的捕捉器 |
handler.isExtensible() | Object.isExtensible 方法的捕捉器 |
handler.preventExtensions() | Object.preventExtensions 方法的捕捉器 |
handler.getOwnPropertyDescriptor() | Object.getOwnPropertyDescriptor 方法的捕捉器。 |
handler.defineProperty() | Object.defineProperty 方法的捕捉器 |
handler.has() | in 操作符的捕捉器 |
handler.get() | 属性读取操作的捕捉器 |
handler.set() | 属性设置操作的捕捉器 |
handler.deleteProperty() | delete 操作符的捕捉器 |
handler.ownKeys() | Object.getOwnPropertyNames 方法和 Object.getOwnPropertySymbols 方法的捕捉器 |
handler.apply() | 函数被apply调用操作的捕捉器 |
handler.construct() | new 操作符的捕捉器 |