Proxy用于修改某些操作的默认行为,等同于在语言层面作出修改,所以属于一种“元编程”,即对编程语言进行编程。
Proxy可以理解成在目标对象前架设的一个“拦截”成,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制可以对外界的访问进行过滤和改写。
proxy作用:比如vue中拦截,预警、上报、扩展功能、统计、增强对象等等
同一个拦截器函数可以设置拦截多个操作:
var handler={
get:function(target,name){
if(target,name){
if(name==='prototype'){
return Object.prototype;
}
return 'Hello,'+name;
}
},
apply:function(target,thisBinding,args){
return args[0];
},
construct:function(target,args){
return {value:args[1]};
}
};
var fproxy=new Proxy(function(x,y){
return x+y;
},handler);
fproxy(1,2);//1
new fproxy(1,2);//2
fproxy.prototype===Object.prototype//true
fproxy.foo//'Hello,foo'
new Proxy(target,hander);
let obj=new Proxy(被代理的对象,对代理的对象做什么操作)
hander:{
set(){},//设置的时候干的事情
get(){},/获取干的事情
deleteProperty(){},//删除
has(){},//有没有这个东西
apply(){}//调用处理
…
}
get()
var person={
name:"远方"
}
var proxy=new Proxy(person,{
get:function(target,property){
if(property in target){
return target[property];
}else{
throw new ReferenceError("error");
}
}
});
proxy.name//"远方"
proxy.age//"error"
//如果没有拦截器,访问不存在的属性只会返回undefined
利用get拦截实现一个生成各种DOM节点的通用函数dom
const DOM=new Proxy({},{
get(target,property){
//property是DOM.xxx的xxx
return function(attr={},...children){
const el=document.createElement(property);
for(let key of Object.keys(attr)){
el.setAttribute(key,attr[key]);
}
for(let child of children){
if(typeof child=='string'){
child =document.createTextNode(child);
}
el.appendChild(child);
}
return el;
}
}
});
let oDiv=DOM.div({id:'div1'},'hello','world',
DOM.a({url},'访问'));
window.onload=function(){
document.body.appendChild(oDiv);
}
set()
set方法用于拦截某个属性的赋值操作
let validator={
set(target,prop,value){
if(prop==='age'){
if(!Number.isInteger(value)){
throw new TypeError('年龄必须为整数');
}
if(value>200){
throw new RangeError('年龄不合法');
}
}
target[prop]=value;
}
}
let person=new Proxy({},validator);
person.age=210;
console.log(person.age);
apply()
apply方法拦截函数的调用、call和apply操作
function fn(){
return '这是一个函数';
}
let proxy=new Proxy(fn,{
apply(){
return '函数';
}
})
console.log(proxy());
function sum(a,b){
return a+b;
}
let newSum=new Proxy(sum,{
apply(target,context,args){
return Reflect.apply(...arguments)*2;
}
})
console.log(newSum(2,3));
deleteProperty()和has()
deleteProperty方法用于拦截delete操作,如果这个方法抛出错误或者返回false,当前属性就无法被delete命令删除
has方法用来拦截HasProperty操作,即判断对象是否具有某个属性时,这个方法会生效。
let json={
a:1,
b:2
};
let newJson=new Proxy(json,{
deleteProperty(target,property){
console.log(`您要删除${porperty}属性`)
delete target[property];
},
has(target,property){
console.log(`判断是否存调用has方法`);
return property in target;
}
});
conaole.log('a' in newJson);
delete newJson.a;
console.log(newJson);
construct()
construct方法用于拦截new命令。
var proxy=new Proxy(function (){},{
construct:function(target,args){
console.log('called:'+args.join(', '));
return {value:args[0]*10};//return返回值必须是一个对象,否则会报错
}
});
(new proxy(1).value);
//"called:1"
//10
defineProperty()
defineProperty方法可以拦截Object.defineProperty操作
var handler={
defineProperty(target,key,descriptor){
return false;
}
};
var target={};
var proxy=new Proxy(target,handler);
proxy.foo=bar;//TypeError
getOwnPropertyDescriptor()
getOwnPropertyDescriptor方法拦截Object.getOwnPropertyDescriptor(),返回一个属性描述对象或者undefined
var handler={
getOwnPropertyDescriptor(target,key,descriptor){
if(key[0]==='_'){
return;
}
return Object.getOwnPropertyDescriptor(target,key);
}
};
var target={baz:'tar'};
var proxy=new Proxy(target,handler);
Object.getOwnPropertyDescriptor(proxy,'baz');
//{value:'tar',writable:true,enumerable:true,configurable:true}
getPrototypeOf()
getPrototypeOf方法主要用来拦截获取对象的原型。
var proto={};
var proxy=new Proxy({},{
getPrototypeOf(target){
return proto;
}
});
Object.getPrototypeOf(proxy)===proto//true
isExtensible()
isExtensible方法拦截Object.isExtensible操作。
var proxy=new Proxy({},{
isExtensible:function(traget){
console.log("called");
return true;
}
});
Object.isExtensible(proxy);
//"called"
//true
ownKeys()
ownKeys方法用来拦截对象自身属性的读取操作。
let target={
a:1,
b:2,
c:3
};
let handler={
ownKeys(target){
return ['a'];
}
};
let proxy=new Proxy(target,handler);
Object.keys(proxy);//['a']
preventExtensions()
preventExtensions方法拦截Object.preventExtensions()。
这个方法有一个限制,只有目标对象不可扩展,proxy.preventExtensions才能返回true,否则会报错。
let proxy=new Proxy({},{
preventExtensions:function(target){
console.log('called');
Object.preventExtensions(target);
return true;
}
});
Object.preventExtensions(proxy);
//"called"
//true
setPrototypeOf()
setPrototypeOf方法主要用于拦截Object.setPrototypeOf方法。
var proto={};
var proxy=new Proxy(function(){},{
setPrototypeOf(target,proto){
throw new Error("改变属性不合法")
}
});
Object.setPrototypeOf(proxy,proto);
//Error:改变属性不合法
Proxy.revocable()
Proxy.revocable方法返回一个可取消的Proxy实例。
let {proxy,revoke}=Proxy.revocable({},{});
proxy.foo=123;
proxy.foo//123;
revoke();
proxy.foo//TypeError