Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。
讲通俗一点就是扩展(增强)了对象,方法(函数)的一些功能
ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。
Proxy其实是设计模式的一种,代理模式
直接通过操作代理对象来操作原对象
new Proxy(target,handle)
- 参数
第一个参数: target 是你要代理的对象
第二个参数,handle是对代理对象做什么操作
{
set(){},
get(){},
deleteProperty(){},
has(){},
apply(),
…
}
- 返回值
返回一个新的对象
let obj = new Proxy(target,handle)
let proxy = new Proxy(target, handler);
Proxy 对象的所有用法,都是上面这种形式,不同的只是handler
参数的写法。其中,new Proxy()
表示生成一个Proxy
实例,target
参数表示所要拦截的目标对象,handler
参数也是一个对象,用来定制拦截行为。
如果handler
没有设置任何拦截,那就等同于直接通向原对象。
let target = {};
let handler = {};
let proxy = new Proxy(target, handler);
proxy.a = 'b';
console.log(target); // { a: 'b' }
上面代码中,handler
是一个空对象,没有任何拦截效果,访问proxy
就等同于访问target
。
let obj = {
name : 'Tom'
};
// 我希望你在获取name 属性的时候做一些事情,那么我们就可以用代理模式
let proxy = new Proxy(obj,{
get(target,property){ // target就是代理对象obj,property就是用户访问的属性
console.log(`你访问了${property}属性`);
return target[property];
}
});
// 获取值得时候默认执行proxy.get 方法
console.log(proxy.name);
// 你访问了name属性
// Tom
let obj = {
name: 'dou',
age: 18
};
let newObj = new Proxy(obj, {
set(target, prop, value) {
console.log(target, prop, value); // obj, age, 18.6
if (!Number.isInteger(value)) {
console.log('年龄必须为整数')
}
}
});
newObj.age = 18.6;
let obj = {
a: 1,
b: 2
};
let proxy = new Proxy(obj, {
deleteProperty(target,prop){
console.log(`你要删除${prop}属性`);
delete target[prop]
}
});
delete proxy.a;
console.log(obj); // {b: 2}
var obj = {
a: 1,
b: 2
}
var newObj = new Proxy(obj,{
has(target,prop){
console.log(`判断属性${prop}是否存在于${target}对象中`)
return prop in target
}
})
console.log('a' in newObj);
// 判断属性a是否存在于[object Object]对象中
// true
function fn(){
return '哈哈'
}
var newFn = new Proxy(fn,{
apply(){
return '我已经拦截了函数'
}
})
console.log(newFn()); //我已经拦截了函数
// 代理函数newFn执行的结果就是apply执行的返回值
此时就已经拦截了函数,但是原来的函数却没有执行,
我们通常希望你拦截函数是你拦截的,但是原函数还是要执行,这就需要配合reflect 反射使用
Proxy.revocable
方法返回一个可取消的 Proxy 实例。
let target = {};
let handler = {};
// proxy 指向target; revoke是一个可取消代理的函数
let {proxy, revoke} = Proxy.revocable(target, handler);
proxy.foo = 123;
console.log(proxy);
// 取消代理
revoke();
Proxy.revocable
方法返回一个对象,该对象的proxy
属性是Proxy
实例,revoke
属性是一个函数,可以取消Proxy
实例。上面代码中,当执行revoke
函数之后,再访问Proxy
实例,就会抛出一个错误。
Proxy.revocable
的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。
proxy.foo
和proxy['foo']
。proxy.foo = v
或proxy['foo'] = v
,返回一个布尔值。propKey in proxy
的操作,返回一个布尔值。delete proxy[propKey]
的操作,返回一个布尔值。Object.getOwnPropertyNames(proxy)
、Object.getOwnPropertySymbols(proxy)
、Object.keys(proxy)
,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()
的返回结果仅包括目标对象自身的可遍历属性。Object.getOwnPropertyDescriptor(proxy, propKey)
,返回属性的描述对象。Object.defineProperty(proxy, propKey, propDesc)
、Object.defineProperties(proxy, propDescs)
,返回一个布尔值。Object.preventExtensions(proxy)
,返回一个布尔值。Object.getPrototypeOf(proxy)
,返回一个对象。Object.isExtensible(proxy)
,返回一个布尔值。Object.setPrototypeOf(proxy, proto)
,返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。proxy(...args)
、proxy.call(object, ...args)
、proxy.apply(...)
。new proxy(...args)
。function fn(a,b){
return a+b
}
var newFn = new Proxy(fn,{
apply(target,context,args){
// console.log(target,context,args);
// console.log(arguments);
// console.log(...arguments);
return Reflect.apply(...arguments);
}
})
console.log(newFn(3,2)); // 5
如果要增强方法需要跟Reflect配合
我也可以在反射时干些事情
var newFn = new Proxy(fn,{
apply(target,context,args){
// console.log(target,context,args);
// console.log(arguments);
// console.log(...arguments);
return Reflect.apply(...arguments)**3; //反射结果的3次方
}
})
console.log(newFn(3,2)); // 125
和fn.apply()很相似
Reflect.apply(target,context,args) 有三个参数
target: 需要调用的函数
context: this指向
args : 参数数组
console.log(Math.ceil(4.4)); // 向上取整 5
// 反射调用Math.ceil 没有this指向,传入了null, 参数数组
let num = Reflect.apply(Math.ceil,null,[5.1]);
console.log(num); // 6
就是调用函数的不同的方式而已
function show(...args){
console.log(this);
console.log(args);
}
// 正常调用
show(1,2,3,4); // this是window, args是[1,2,3,4]
// call调用函数
show.call('aaa',1,2,3,4); // this是aaa, args是[1,2,3,4]
// apply调用函数
show.apply('aaa',[1,2,3,4]); // this是aaa, args是[1,2,3,4]
// reflect.apply调用函数
Reflect.apply(show,'aaa',[1,2,3,4]); // this是aaa, args是[1,2,3,4]
通过reflect拿到语言内部的东西