在ES6中,使用Proxy来代理一个对象,代理一个对象:当我们想要访问一个对象的时候,并不直接访问这个对象,而是先访问它的Proxy 。通过Proxy,可以给对象添加一层拦截,任何对这个对象的访问行为,都会经过这层拦截。
Proxy的本质是复制原对象,形成一个副本,随后访问的人只能对副本进行操作,而不是原对象进行操作.Proxy在复制副本的时候,还可以进行对一些行为进行定义。
语法:
const p = new Proxy (target,handler)
定义代理对象p, p就是一个Proxy对象。Proxy是一个构造函数,它接收两个参数,其中target表示需要代理的原对象,handler表示配置对象。
配置对象中可以使用各种方法来针对原对象的各种操作进行拦截,常用的拦截方法如下:
get(): 拦截 "读" 的操作
set(): 拦截 "写" 的操作
has(): 拦截 in 操作
deleteProperty(): 拦截delete操作
ownKeys(): 拦截遍历操作
<script>
// 原对象person1
const person1 = {
name:"张三",
gender:"男",
_age:13 // 注意: _age 是下划线开头的,表示age属性是私有属性
}
// 配置对象,当读取不存在的属性时候,抛出错误
const handler1 = {
// get()方法拦截对对象的"读"操作
get(obj,key,proxy){
// obj是原对象person,key是属性名 ,proxy是代理对象
if(obj.hasOwnProperty(key)){
// 判断属性是否存在于原对象中
return obj[key]
}else{
throw new Error("属性不存在")
}
}
}
//创建Proxy对象
const p1 = new Proxy(person1,handler1);
console.log(person1); // {name: '张三', gender: '男', _age: 13}
console.log(p1); // Proxy(Object) {name: '张三', gender: '男', _age: 13}
console.log(p1.name); // 张三
console.log(person1.name); // 张三
console.log(p1._age); // 13
console.log(person1._age); // 13
p1.name="赵六";
p1._age=14;
console.log(person1); // {name: '赵六', gender: '男', _age: 14}
console.log(p1); // Proxy(Object) {name: '赵六', gender: '男', _age: 14}
// console.log(p1.city) // Uncaught Error: 属性不存在
</script>
<script>
// 原对象person2
const person2 = {
name:"李四",
gender:"男",
_age:14 // 注意: _age 是下划线开头的,表示age属性是私有属性
}
// 配置对象, 禁止读取私有属性,当读取私有属性时候,抛出错误;禁止修改私有属性的值,当修改私有属性的值的时候,抛出错误
const handler2 = {
// get()方法拦截对对象的"读"操作
get(obj,key,proxy){
// obj是原对象person,key是属性名 ,proxy是代理对象
if(key.startsWith("_")){
throw new Error("私有属性不允许被访问")
}else{
return obj[key]
}
},
// set()方法拦截对对象的"写"操作
set(obj,key,newvalue){
if(key.startsWith("_")){
throw new Error("私有属性不允许被修改")
}else{
obj[key] = newvalue
}
},
// has()方法拦截对对象的in的操作,当我们使用in操作符来判断对象中是否有该属性的时候,自动触发has()方法,has()方法返回是一个布尔值,一般是 return key in obj
has(obj,key){
if(key.startsWith("_")){
throw new Error("禁止访问私有属性")
}else{
return key in obj
}
},
// deleteProperty()方法拦截对象的delete操作
deleteProperty(obj,key){
if(key.startsWith("_")){
throw new Error("禁止删除私有属性")
}else{
delete obj[key]
return true
}
},
//用ownKeys()方法来拦截针对对象属性的遍历操作,禁止遍历私有属性
ownKeys(obj){
console.log('触发遍历操作');
const res = Object.keys(obj).filter((item) =>{
// Object.keys(obj)返回的是原对象的所有的key组成的数组,然后用filter()方法过滤,当数组中的item不是以_开头的就返回,用变量res接收,res是一个新数组,因为filter()方法的返回值是一个数组,将符合条件的item 添加到res中
return !item.startsWith("_")
});
return res;
},
};
// 当读取私有属性时候,抛出错误,读取其他属性的时候可以正常读取,当修改私有属性的值的时候,抛出错误,修改其他属性的值的时候,可以正常修改。
//创建Proxy对象
const p2 = new Proxy(person2,handler2);
console.log(person2); //{name: '李四', gender: '男', _age: 14}
console.log(p2); // Proxy(Object) {name: '李四', gender: '男', _age: 14}
// 读取代理对象p2的属性,会自动触发handler配置对象中的get()方法
console.log(p2.name); // 李四
console.log(person2.name); // 李四
// console.log(p2._age); // Uncaught Error: 私有属性不允许被访问
console.log(person2._age); // 14
// 修改代理对象p2的属性的值,会自动触发handler配置对象中的set()方法
p2.name="王八";
// p2._age=18; // Uncaught Error: 私有属性不允许被修改
// 代理对象p2中是否有该属性,自动触发handler配置对象中的has()方法,有就返回true,没有就false
console.log("name" in p2) // true
console.log("gender" in p2) // true
// console.log("_age" in p2) // Uncaught Error: 禁止访问私有属性
// 删除代理对象p2的属性
// delete p2._age; // Uncaught Error: 禁止删除私有属性
console.log(p2); // Proxy(Object) {gender: '男', _age: 14}
// ownKeys()方法来拦截针对对象属性的遍历操作,ownKeys()方法主要拦截以下操作:
/*
Object.keys()
Object.getOwnPropertyNames()
Object.getOwnPropertySymbols()
Reflect.ownKeys() */
const keyArr1 = Object.keys(p2)
const keyArr2 = Object.getOwnPropertyNames(p2)
console.log(keyArr1) // ['name', 'gender']
console.log(keyArr2) // ['name', 'gender']
</script>