proxy 代理

Proxy 代理

Proxy 用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程”,即对编程语言进行编程。

讲通俗一点就是扩展(增强)了对象,方法(函数)的一些功能

ES6 原生提供 Proxy 构造函数,用来生成 Proxy 实例。

Proxy其实是设计模式的一种,代理模式

1. 语法使用

直接通过操作代理对象来操作原对象

new Proxy(target,handle)

  1. 参数

第一个参数: target 是你要代理的对象

第二个参数,handle是对代理对象做什么操作

{

​ set(){},

​ get(){},

​ deleteProperty(){},

​ has(){},

​ apply(),

​ …

}

  1. 返回值

返回一个新的对象

let obj = new Proxy(target,handle)

let proxy = new Proxy(target, handler);

Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

1.1 如果没有做任何拦截设置

如果handler没有设置任何拦截,那就等同于直接通向原对象。

let target = {};
let handler = {};
let proxy = new Proxy(target, handler);
proxy.a = 'b';
console.log(target);  // { a: 'b' }

上面代码中,handler是一个空对象,没有任何拦截效果,访问proxy就等同于访问target

2. get(){} 获取时拦截
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
3.set(){} 设置时拦截
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;
4. deleteProperty (){} 删除属性时 拦截
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}
5. has(){} 在判断是否存在属性时拦截
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
6.apply(){} 拦截方法的时候使用
function fn(){
    return '哈哈'
}
var newFn = new Proxy(fn,{
    apply(){
        return '我已经拦截了函数'
    }

})

console.log(newFn());  //我已经拦截了函数
// 代理函数newFn执行的结果就是apply执行的返回值

此时就已经拦截了函数,但是原来的函数却没有执行,

我们通常希望你拦截函数是你拦截的,但是原函数还是要执行,这就需要配合reflect 反射使用

7. 取消代理 Proxy.revocable()

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的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

8. Proxy支持的拦截操作
  • get(target, propKey, receiver):拦截对象属性的读取,比如proxy.fooproxy['foo']
  • set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = vproxy['foo'] = v,返回一个布尔值。
  • has(target, propKey):拦截propKey in proxy的操作,返回一个布尔值。
  • deleteProperty(target, propKey):拦截delete proxy[propKey]的操作,返回一个布尔值。
  • ownKeys(target):拦截Object.getOwnPropertyNames(proxy)Object.getOwnPropertySymbols(proxy)Object.keys(proxy),返回一个数组。该方法返回目标对象所有自身的属性的属性名,而Object.keys()的返回结果仅包括目标对象自身的可遍历属性。
  • getOwnPropertyDescriptor(target, propKey):拦截Object.getOwnPropertyDescriptor(proxy, propKey),返回属性的描述对象。
  • defineProperty(target, propKey, propDesc):拦截Object.defineProperty(proxy, propKey, propDesc)Object.defineProperties(proxy, propDescs),返回一个布尔值。
  • preventExtensions(target):拦截Object.preventExtensions(proxy),返回一个布尔值。
  • getPrototypeOf(target):拦截Object.getPrototypeOf(proxy),返回一个对象。
  • isExtensible(target):拦截Object.isExtensible(proxy),返回一个布尔值。
  • setPrototypeOf(target, proto):拦截Object.setPrototypeOf(proxy, proto),返回一个布尔值。如果目标对象是函数,那么还有两种额外操作可以拦截。
  • apply(target, object, args):拦截 Proxy 实例作为函数调用的操作,比如proxy(...args)proxy.call(object, ...args)proxy.apply(...)
  • construct(target, args):拦截 Proxy 实例作为构造函数调用的操作,比如new proxy(...args)
9.reflect 反射
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
9.1 Reflect.apply()

和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拿到语言内部的东西

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值