数据代理
- what?
- 通过一个对象代理对另一个对象中的属性的操作(读/写)
- 数据代理是官方给的说法(名字) , 但是在社区里大家还是都叫数据劫持。因为大家都习惯了这个叫法,而且这个行为也是一个劫持行为。是 ES6 提供的语法 , 是一个内置构造函数。
-
方法:利用 ES6 新推的一个内置构造函数
-
语法: const 变量名 = new Proxy(要代理的原始对象,{ 配置项 })
-
返回值: 一个实例对象 , 就是代理结果数据
-
vue3 中新增的方式,之前的数据劫持方式为 vue2 中内容
案例1
基础 var o={};
new Proxy(目标对象,触发手柄对象) 返回一个代理对象
当修改代理对象时,就会触发修改目标对象
var p=new Proxy(o,{});
p.a=10;
console.log(o)
Reflect 这个类是对象的反射类,静态方法内容
升级 var o={a:1,b:2};
var p=new Proxy(o,{
// target 就是目标对象o
// key 设置的属性名
// value 设置的属性值
// 任何一个事件手柄方法都需要返回一个值
set(target,key,value){
//写法1 if(Object.hasOwn(target,key)){
// target[key]=value;
// return true;
// }
// return false;
/*
Reflect.set(target,key,value):
给目标对象target设置key属性的值为value
设置成功时 ,不光值改变,还会返回true
Object.hasOwn(对象,属性名) 判断这个属性名是不是对象的对象属性
如果不是对象里面的属性就是false
*/
写法2 if(!Object.hasOwn(target,key)) return false;
return Reflect.set(target,key,value)
}
})
案例2
var obj={
name:"xietian",
age:30,
sex:"男",
tel:"18617890987",
email:"10398975@qq.com"
}
// 设置一个代理对象,只能修改年龄,邮箱,电话,其他属性不能修改也不能添加新的属性
var p=new Proxy(obj,{
set(target,key,value){
if(/age|tel|email/.test(key)){
return Reflect.set(target,key,value);
}
return false;
},
// get 目标 属性名 获取属性
get(target,key){
//1. return Reflect.get(target,key); 作用就是下面的2
//2. return target[key]
// 只要打印的不是这三个属性就输出空元素
if(/name|age|sex/.test(key)){
return Reflect.get(target,key);
}
如果传入的不是这三个属性返回一个空元素
return null;
}
})
p.age=40;
p.name="zhangsan"
p.tel="18790786543"
console.log(obj)
console.log(p.age);
console.log(p.name);
console.log(p.sex);
console.log(p.tel);
案例3
var div=document.querySelector("div");
var age=new Proxy(div,{
// p就是代理对象
set(target,key,value,p){
if(key==="value"){
if(!p.prev) p.prev=target.textContent.split("{")[0]
if(!p.next) p.next=target.textContent.split("}").pop();
// 重新修改属性值渲染页面
return Reflect.set(target,"textContent",p.prev+value+p.next);
}else if(/prev|next/.test(key)){
return Reflect.set(target,key,value);
}
},
get(target,key){
if(key==="value"){
// console.log(target.prev,target.next,target.textContent)
// return Reflect.get()
var txt=target.textContent.replace(target.prev,"");// 30岁了
txt=txt.replace(target.next,"");//30
return Number(txt);
}
return Reflect.get(target,key);
}
})
age.value=0;
console.log(age.value)
setInterval(function(){
age.value++;
},1000);
代理的一些属性
set和defineProperty同时使用时的注意点
var obj={a:1,b:2};
// set和defineProperty同时使用时,如果仅仅设置值,则触发set,不触发defineProperty
var p=new Proxy(obj,{
defineProperty(target,key,desc){
// Object.defineProperty(target,key,desc)
desc.enumerable=false;
return Reflect.defineProperty(target,key,desc);
}
})
p.a=10;
Object.defineProperty(p,"c",{
enumerable:false,
configurable:true,
writable:true,
value:3
})
p.a=10;
p.c=20;
console.log(obj)
删除
var obj={a:1,b:2,c:3};
var p=new Proxy(obj,{
deleteProperty(target,key){
// delete target[key]
// key 属性是a时 ,无法删除
if(key==="a") return false;
return Reflect.deleteProperty(target,key);
}
})
delete p.a;
delete p.b;
console.log(obj)
返回对象属性的描述对象
var obj={a:1,b:2,c:3};
var p=new Proxy(obj,{
getOwnPropertyDescriptor(target,key){
// console.log(key)
if(key==="a") return undefined;//只能返回undefined
// return Object.getOwnPropertyDescriptor(target,key)
return Reflect.getOwnPropertyDescriptor(target,key);
}
})
// var desc=Object.getOwnPropertyDescriptor(p,"b");
var descs=Object.getOwnPropertyDescriptors(p);
console.log(descs)
当获取原型链或者设置原型链
var o={a:1,b:2};
var o1=Object.create(o);
o1.c=3;
o1.d=4;
当获取原型链或者设置原型链
var p=new Proxy(o1,{
getPrototypeOf(target){
console.log("aaa")
// return null; 无法获取原型链
return Reflect.getPrototypeOf(target);
},
setPrototypeOf(target,proto){
return true;//返回true跳过设置原型链,不修改原型链
// console.log("bbb")
return Reflect.setPrototypeOf(target,proto);
}
})
// 这两个调用都会激活getPrototypeOf
// console.log(Object.getPrototypeOf(p))
// console.log(p.__proto__);
// 这两个调用都会激活setPrototypeOf
var o2={abc:10};
Object.setPrototypeOf(p,o2);
// p.__proto__=o2;
console.log(o1)
判断对象属性和原型链属性
var obj={a:1,b:2,c:3};
var o1=Object.create(obj);
o1.d=4;
o1.e=5;
var p=new Proxy(o1,{
has(target,key){
// console.log("aa");
// return Reflect.has(target,key);
if(Object.hasOwn(target,key)){
return Reflect.has(target,key);
}
return false;
}
})
使用 in 判断属性是否在对象中存在,可以判断对象属性和原型链属性
可以使用has种条件判断是否返回是对象属性还是原型链属性
console.log("d" in p);
console.log("a" in p)
获取到元素的key列表
var obj={a:1,b:2,c:3};
var p=new Proxy(obj,{
ownKeys(target){
console.log("aa")
// 获取到元素的key列表后不能添加只能删除后返回
var list=Reflect.ownKeys(target);
list.pop();
return list;
// return Reflect.ownKeys(target);
}
})
下面这5个方法都会触发这个内容
console.log(Object.keys(p))
console.log(Object.values(p))
console.log(Object.getOwnPropertyNames(p))
console.log(Object.getOwnPropertySymbols(p))
console.log(Reflect.ownKeys(p))
产生一个代理函数
var obj={
a:1,
getSum(b){
return this.a+b;
}
}
产生一个代理函数
var fn=new Proxy(obj.getSum,{
apply(target,thisArg,argArray){
// console.log(thisArg) //代表this指向
thisArg=obj;//固定this指向用于 console.log(fn(1))
return Reflect.apply(target,thisArg,argArray);
}
})
console.log(fn(1))
console.log(fn.call(obj,3))
console.log(fn.apply(obj,[3]))