1.Proxy的定义:
Proxy即名为代理,所谓代理就是你要获取或者操作某个东西,通过这个代理中介帮你去做,而不是直接作用于这个对象上。就像在网上买东西一样,通过某猫,某东平台买,换货也是通过平台帮你去换。
Proxy是一种特殊的对象,它提供了一种机制来拦截和自定义对目标对象的操作。
2.Proxy使用方式:
let p=new Proxy(target,handler)
target:用Proxy包装的目标对象(可以是数组、对象,函数等等)
handler:一个对象,拦截过滤代理操作的函数。
注意:要使Proxy其作用,要针对Proxy实例(也就是第二个参数)进行操作。而不是针对目标对象进行操作。
3.proxy代理方式
(1)get(target,prop,receiver):拦截对象属性的访问
(2)set(target,prop,value,receiver):拦截对象属性的设置,最后返回一个布尔值
(3)apply(target,object,agrments):用于拦截函数的调用,比如proxy()
(4) construct(target,agrments):用于拦截new操作符,比如new proxy(),为了使new操作符在生成的proxy对象上生效。
(5)has(target,prop):拦截例如prop in proxy 的操作,返回一个布尔值
(6)deleteProperty(target,prop):拦截例如 delete proxy[prop]的操作,返回一个布尔值
(7)ownKeys(target):拦截Object.getOwnPropertyNames(proxy)、Object.keys(proxy)、for in循环操作等等,返回一个数组。
(8) defineProperty(target,propkey,propDes):拦截Object.defineProperty(proxy,propkey,propDes)
(9) getOwnPropertyDescriptor(target,prop):拦截Object.getOwnPropertyDescriptor(proxy,propkey)
(10) preventExtensions(target):拦截Object.preventExtensions(proxy)
(11) getPrototypeOf(target):拦截Object.getPrototypeOf(proxy)
(12) isExtensible(target):拦截Object.isExtensible(proxy)
(13) setPrototypeOf(target):拦截Object.setPrototypeOf(proxy)
let target = {
name: 'Alice',
age: 25
};
// 处理器对象,定义拦截行为
let handler = {
get(target, propKey, receiver) {
console.log(`Reading ${propKey}`);
return Reflect.get(...arguments);
},
set(target, propKey, value, receiver) {
console.log(`Writing ${propKey}`);
return Reflect.set(...arguments);
}
};
// 创建代理对象
let proxy = new Proxy(target, handler);
// 读取代理对象的属性
console.log(proxy.name); // 输出: Reading name, 然后输出: Alice
// 修改代理对象的属性
proxy.age = 30; // 输出: Writing age
// 检查属性是否存在
console.log('age' in proxy); // 输出: Reading age, 然后输出: true
// 删除代理对象的属性
delete proxy.age; // 输出: Writing age, 但不会真的删除,因为handler中没有定义deleteProperty陷阱
4.使用Object.defineProperty实现简单的数据双向绑定
在vue2.0中,数据的绑定是使用es5中的Object.defineProperty。遍历data函数对象中的每个属性,绑定set,get方法,拦截监听数据的变化。
let obj={
age:18
}
Object.defineProperty(obj, 'age', {
get: function() {
console.log('get===1')
return this._age
},
set: function(value) {
this._age = value*3
}
});
obj.age=9 //会触发set方法
console.log(obj.age);// 会触发get方法
console.log(obj)// 输出:{age:27,_age:27} ,不能直接this.age 会报错,需要新建一个属性名去改变原有属性
<body>
<div>
姓名:<input type="text" id="input">
</div>
<p id="p"></p>
</body>
<script>
const input=document.getElementById('input')
const p=document.getElementById('p')
const obj={
name:''
}
input.addEventListener('input',function(e){
obj.name=e.target.value
})
Object.defineProperty(obj,'name',{
set:function(value){
input.value=value
p.innerHTML=value
this._name=value
},
get:function(){
return this._name
}
})
</script>
4.Object.defineProperty() 对比 Proxy 有什么区别?
1.Object.defineProperty()无法一次性监听对象所有属性,必须遍历或者递归来实现。而Proxy的实现就不需要了。
let target = {
name: 'Alice',
age: 25
};
Object.keys(target).forEach(key => {
Object.defineProperty(target,key,{
set:function(value){
this[`_`+key]=value
},
get:function(){
return this[`_`+key]
}
})
});
target.age=30
target.name='wufang'
target.name
2.Object.defineProperty()无法监听新增的属性,需要手动再添加新的属性监听。而Proxy则可以监听新增属性。vue2中动态监听新增的属性用Vue.set(obj,key,value)方法。
let girl = {
name: 'Alice',
age: 25
};
Object.keys(girl).forEach(key => {
Object.defineProperty(girl,key,{
set:function(value){
console.log(value)
},
get:function(){
}
})
});
girl.age=30 //会触发set方法
girl.hobby='打篮球' //不会触发set方法
//手动再添加新的属性监听
Object.defineProperty(girl,'hobby',{
set:function(value){
console.log(value)
},
get:function(){
}
})
girl.hobby='打篮球' //会触发set方法
let girl = {
name: 'Alice',
age: 25
};
let proxy = new Proxy(girl, {
get(target, propKey, receiver) {
console.log(`Reading ${propKey}`);
return Reflect.get(...arguments);
},
set(target, propKey, value, receiver) {
console.log(`Writing ${propKey}`);
return Reflect.set(...arguments);
}
});
proxy.hobby='打篮球' //会触发set方法
3.Object.defineProperty 可以监听数组的变化,但是无法对数组的变化响应。但Proxy则可以
let arr=[1,2,3]
arr.forEach((item,index)=>{
Object.defineProperty(arr,`${index}`,{
set:function(value){
},
get:function(){
}
})
})
arr[0]=9 //会触发set方法
arr[4]=19 //不会触发set方法
let arr=[1,2,3]
let proxy = new Proxy(arr, {
get(target, propKey, receiver) {
console.log(`Reading ${propKey}`);
return Reflect.get(...arguments);
},
set(target, propKey, value, receiver) {
console.log(`Writing ${propKey}`);
return Reflect.set(...arguments);
}
});
proxy[0]=9 //会触发set方法
proxy[4]=19 //会触发set方法
4.Proxy的应用
表单的校验
const nameEl=document.getElementById('name')
const passwordEl=document.getElementById('password')
let rules={
name:{
validate(value){
return value.length>6
},
message:'用户名长度不能小于6位'
},
password:{
validate(value){
return value.length>10
},
message:'密码长度不能小于10位'
}
}
function validators(obj,rules){
return new Proxy(obj,{
set(target,key,value){
let validator=rules[key]
if(!validator){
return target[key]
}else if(validator.validate(value)){
return target[key]
}else{
alert(validator.message)
}
},
get(){}
})
}
let form={}
form=validators(form,rules)
nameEl.addEventListener('blur',function(e){
form.name=e.target.value
})
passwordEl.addEventListener('blur',function(e){
form.password=e.target.value
})
原文链接:https://juejin.cn/post/7342743955315900466 作者:奶茶不加冰七分甜