js语法 proxy对象拦截方法详解,proxy代理一个对象(数组,函数)的操作方法
如果还不能理解什么是proxy代理可以参考:js语法---理解反射Reflect对象和代理Proxy对象_js代理对象-CSDN博客
proxy代理对象(数组,函数),可以拦截的操作
- apply---拦截方法的执行(包括直接执行,通过apply,call执行),和对应的Reflect.apply
- construct---拦截new关键字,和对应的Reflect.construct
- defineProperty---拦截拦截目标对象的以下操作:Object.defineProperty , proxy.property='value',和对应的Reflect.defineProperty,
- deleteProperty---拦截delete 关键字,和对应的Reflect.deleteProperty
- get---拦截所有读取属性的操作(包括直接读取,访问原型属性),和对应的Reflect.get
- getOwnPropertyDescriptor---拦截方法Object.getOwnPropertyDescriptor,Reflect.getOwnPropertyDescriptor
- getPrototypeOf---拦截对原型的访问(Object.getPrototypeOf,Reflect.getPrototypeOf,Object.prototype.__proto__,Object.prototype.isPrototypeOf,instanceof),
- has--拦截对属性的查询,(in关键字,with检查),和对应的Reflect.has
- ownKeys---拦截读取属性集合,(Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.keys),以及对应的Reflect.ownKeys
- set---拦截设置属性的操作(直接赋值,通过Object.create创建的代理对象赋值),和对应的Refl.set
- setPrototypeOf---拦截Object.setPrototypeOf方法,和对应的Reflect.setPrototypeOf
proxy的拦截主要分为两类:
- 拦截对属性,原型,原型的属性操作;例如,添加/删除属性,读取/修改值,读取/修改原型,
- 拦截特定的方法;例如Object.setPrototypeOf,Object.getOwnPropertyDescriptor,
其中,第一类是比较常用的,第二类是对特定一些方法的拦截,一般情况下用不上,
set和get---拦截属性操作
set和get是代理拦截最常见的两个方法,它们可以在代理对象读取属性和修改属性时执行一些其他操作
get: function (target, key, proxy)
// 代理的目标对象,代理目标的属性,代理对象
set: function(target,key,value,proxy)
// 代理的目标对象,代理目标的属性,本次拦截的修改值,代理对象
以下演示了拦截基本的读取和赋值操作
const obj = {
type: 'object',
toString: () => {
return this.type;
}
}
const p = new Proxy(obj, {
get: function (target, key, proxy) {// 代理的目标对象,代理目标的属性,代理对象(p)
console.log("原目标对象:",target,"\n拦截访问的属性:"+key,"\n触发本次拦截的对象",proxy);// 访问属性时,触发拦截打印
return Reflect.get(target, key);
},
set: function(target,key,value,proxy){
console.log('数据试图被修改成',value,'已拦截此操作');
const result = Reflect.set(target,key,Reflect.get(target,key)); // set,修改key属性的值,返回布尔值
return result
}
});
console.log(p.type);// 直接访问属性,触发get拦截,
p.type = 'Array';
console.log(p.type);// 直接访问属性,触发get拦截,
p是obj对象的代理对象,用p来代替obj操作,当访问p的type属性时触发get拦截,本次的访问结果就变成了get拦截返回的结果,若这里没有返回值,则返回undefined;
当对p的type属性进行赋值时,触发set拦截,set内阻止了赋值,再次打印结果可以看到type的值还是object
(set,get拦截替换了原本的属性访问,修改,如果proxy内没有任何拦截方法,则不会触发代理的效果,即相当于 const p = obj )
construct---拦截new关键字
proxy可以代理拦截对象,而函数(数组)也是对象的一种,所以一样可以来拦截函数的操作,construct拦截可以在函数使用new关键字时执行一些其他操作
construct:function(fn,args,proxy)
// 代理的目标函数,目标函数被调用时的参数列表,代理对象
// 定义一个构造函数
function Fn(name){
this.name = name;
}
const People = new Proxy(Fn,{
construct : (fn,args,proxy)=>{// 代理的目标函数,目标函数被调用时的参数列表,代理对象
const result = new fn(...args);
console.log('正在使用new新建对象',result)
return result;
}
})
const one = new People('Tom')
console.log(one);
construct拦截将原本的new操作拦截了,并由自身代替执行,如果没有返回值则返回undefined
apply---拦截方法的执行
当proxy代理的对象是一个函数时可以设置apply拦截,否则将会产生错误,当函数直接执行时,或使用apply,call 替换执行时 触发拦截执行一些操作,
apply:function(fn,thisArg,args)
// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
const add = (a,b)=>a+b;
const Add = new Proxy(add,{
apply:(fn,thisArg,args)=>{// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
console.log(fn,thisArg,args);
return fn(...args);
}
})
console.log(Add(1,2));
let f = '代理执行';
Add.apply(f,[2,3]);// apply方法 将Add函数内部的this指向修改成f
拦截原本的函数执行(阻止原函数执行,替换为自身执行),如果没有返回值则返回undefined
ownKeys---拦截读取属性集合
当读取对象的所有属性时(通常时Object.keys方法),会触发ownKeys拦截,执行一些其他操作
ownKeys:function(target)
// 原目标对象
const info = {
a:1,
b:2,
c:3
}
const Info = new Proxy(info,{
ownKeys:(target)=>{// 原目标对象
const result = Reflect.ownKeys(target);
console.log('正在获取所有属性',result)
return result;
}
})
console.log(Object.keys(Info));
for(let key in Info){
}
可以看到除了直接使用Object.keys,for in循环也会获取对象的所有属性,如果没有返回值则返回undefined
deleteProperty---拦截delete 关键字
使用delete删除对象属性时,代理对象会触发deleteProperty拦截
deleteProperty:(target,key)=>{}
// 原目标对象,当前删除的属性
const delObj = {
del:'要删除的属性',
other:'其他内容'
}
const DelObj = new Proxy(delObj,{
deleteProperty:(target,key)=>{// 原目标对象,当前删除的属性
if(key == 'del'){
console.log('成功删除属性'+key);
const result = Reflect.deleteProperty(target,key);
return result;
}else{
console.log('删除失败,属性'+key+'不可删除')
return false;
}
}
})
delete DelObj.del
delete DelObj.other
拦截了delete的删除操作,并指定了可以删除的内容,需要返回布尔值,表示删除的执行成功和失败
has--拦截对属性的查询(in 关键字)
使用in 关键字判断属性是否存在于对象中时触发
has:(target,key)=>{}
// 目标对象,查询的属性
const has = new Proxy({name:'Tom'},{
has:(target,key)=>{// 目标对象,查询的属性
console.log('正在查询属性',key,"是否存在")
return Reflect.has(target,key);
}
})
console.log('name' in has)
console.log('age' in has)
返回查询的结果,默认返回false
getPrototypeOf---拦截对原型的访问
当代理对象触发 JS 引擎读取一个对象的原型时,触发 getPrototypeOf拦截,
以下几种方式都会访问到原型:
- Object.getPrototypeOf()
- Reflect.getPrototypeOf()
- Object.prototype.__proto__
- Object.prototype.isPrototypeOf()
- instanceof
getPrototypeOf拦截的
返回值必须是一个对象或者 null,否则将产生错误
const arr = [0,1,2,3,4];
const Arr = new Proxy(arr,{
getPrototypeOf:(target)=>{// 原目标对象
console.log('访问了'+target+'的原型');
return Reflect.getPrototypeOf(target);
}// 返回值必须是一个对象或者 null,否则将产生错误
})
console.log(Arr instanceof Array)
当返回值为null时结果为false,返回值不为null也不为对象是产生错误
其他的特定拦截
其他拦截属性,都是对特定的方法进行拦截,当代理对象使用这些特定的方法时就会触发对应的拦截,想查看这些特定方法可以参考:Proxy() 构造函数 - JavaScript | MDN (mozilla.org)
总结
proxy的代理功能
proxy可以代理原目标对象执行一些操作,并且这些操作都是可以拦截的;例如赋值,可以在赋值的过程执行其他操作,甚至改变或者阻止赋值;
使用proxy代理可以实现对对象(包括数组,函数,map等引用数据类型)更加精细的控制,掌控对象操作的每一个流程,vue框架的响应式原理就依赖于proxy的代理,当虚拟dom对象执行操作改变时,拦截操作并修改(重新渲染)ui视图;
关于Reflect
Reflect对象提供了proxy拦截属性的对应方法,它的作用是还原拦截的操作,例如,Reflect.construct就是执行原本被拦截的new 关键字操作,使用Reflect可以保证原本的操作可以正常执行,并在此基础上新增操作;
虽然使用其他方法也能达到相同的效果,例如本文construct的例子就是再执行的原构造函数的结果,但是,使用Reflect的方法和proxy的拦截属性方法的参数完全一致(Reflect.construct(fn,args,proxy)),这种用法能够更加简洁的还原原本拦截的操作
完整代码和运行结果
// proxy代理对象(数组,函数),可以拦截的操作:
// apply---拦截方法的执行(包括直接执行,通过apply,call执行),和对应的Reflect.apply
// construct---拦截new关键字,和对应的Reflect.construct
// defineProperty---拦截目标对象的以下操作:Object.defineProperty , proxy.property='value',和对应的Reflect.defineProperty,
// deleteProperty---拦截delete 关键字,和对应的Reflect.deleteProperty
// get---拦截所有读取属性的操作(包括直接读取,访问原型属性),和对应的Reflect.get
// getOwnPropertyDescriptor---拦截方法Object.getOwnPropertyDescriptor,Reflect.getOwnPropertyDescriptor
// getPrototypeOf---拦截对原型的访问(Object.getPrototypeOf,Reflect.getPrototypeOf,Object.prototype.__proto__,Object.prototype.isPrototypeOf,instanceof),
// has--拦截对属性的查询,(in关键字,with检查),和对应的Reflect.has
// ownKeys---拦截读取属性集合,(Object.getOwnPropertyNames,Object.getOwnPropertySymbols,Object.keys),以及对应的Reflect.ownKeys
// set---拦截设置属性的操作(直接赋值,通过Object.create创建的代理对象赋值),和对应的Refl.set
// setPrototypeOf---拦截Object.setPrototypeOf方法,和对应的Reflect.setPrototypeOf
const obj = {
type: 'object',
toString: () => {
return this.type;
}
}
const p = new Proxy(obj, {
get: function (target, key, proxy) {// 代理的目标对象,代理目标的属性,代理对象(p)
console.log("原目标对象:",target,"\n拦截访问的属性:"+key,"\n触发本次拦截的对象",proxy);// 访问属性时,触发拦截打印
return Reflect.get(target, key);
},
set: function(target,key,value,proxy){
console.log('数据试图被修改成',value,'已拦截此操作');
const result = Reflect.set(target,key,Reflect.get(target,key)); // set,修改key属性的值,返回布尔值
return result
}
});
console.log(p.type);// 直接访问属性,触发get拦截,
p.type = 'Array';
console.log(p.type);// 直接访问属性,触发get拦截,
console.log('-----------分割线----------\n')
// 定义一个构造函数
function Fn(name){
this.name = name;
}
const People = new Proxy(Fn,{
construct : (fn,args,proxy)=>{// 代理的目标函数,代理函数被调用时的参数列表,代理对象
const result = new fn(...args);
console.log('正在使用new新建对象',result)
return result;
}
})
const one = new People('Tom')
console.log(one);
console.log('-----------分割线----------\n')
const add = (a,b)=>a+b;
const Add = new Proxy(add,{
apply:(fn,thisArg,args)=>{// 代理的目标函数,函数执行时的this的指向(默认指向全局,值为undefined),代理函数被调用时的参数列表,
console.log(fn,thisArg,args);
return fn(...args);
}
})
console.log(Add(1,2));
let f = '代理执行';
Add.apply(f,[2,3]);// apply方法 将Add函数内部的this指向修改成f
console.log('-----------分割线----------\n')
const info = {
a:1,
b:2,
c:3
}
const Info = new Proxy(info,{
ownKeys:(target)=>{// 原目标对象
const result = Reflect.ownKeys(target);
console.log('正在获取所有属性',result)
return result;
}
})
console.log(Object.keys(Info));
for(let key in Info){
}
console.log('-----------分割线----------\n')
const delObj = {
del:'要删除的属性',
other:'其他内容'
}
const DelObj = new Proxy(delObj,{
deleteProperty:(target,key)=>{// 原目标对象,当前删除的属性
if(key == 'del'){
console.log('成功删除属性'+key);
const result = Reflect.deleteProperty(target,key);
return result;
}else{
console.log('删除失败,属性'+key+'不可删除')
return false;
}
}
})
delete DelObj.del
delete DelObj.other
console.log(DelObj)
console.log('-----------分割线----------\n')
const has = new Proxy({name:'Tom'},{
has:(target,key)=>{// 目标对象,查询的属性
console.log('正在查询属性',key,"是否存在")
return Reflect.has(target,key);
}
})
console.log('name' in has)
console.log('age' in has)
console.log('-----------分割线----------\n')
const arr = [0,1,2,3,4];
const Arr = new Proxy(arr,{
getPrototypeOf:(target)=>{// 原目标对象
console.log('访问了'+target+'的原型');
return Reflect.getPrototypeOf(target);
}// 返回值必须是一个对象或者 null,否则将产生错误
})
console.log(Arr instanceof Array)
Arr.push(5)
原文地址:https://blog.csdn.net/I_am_shy/article/details/140672173
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!