proxy 代理

it2024-10-05  39

Proxy 代理

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

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

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

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

1. 语法使用

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

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参数也是一个对象,用来定制拦截行为。

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

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

var target = {}; var handler = {}; var proxy = new Proxy(target, handler); proxy.a = 'b'; target.a // "b"

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

let obj = { name : 'wuwei' } console.log(obj.name); // 我希望你在获取name 属性的时候做一些事情,那么我们就可以用代理模式 let newObj = new Proxy(obj,{ get(target,property, newObj){ // target就是代理对象obj,property就是用户访问的属性 // console.log(target,property); // {name: "wuwei"} "aaa" console.log(`你访问了${property}属性`) return target[property]; } })

2. get(){} 获取时拦截

let obj = { name: 'dou', age: 18 } let newObj = new Proxy(obj, { get(target, property) { if (property in target) { return target[property] } else { return `${property} 属性不存在` } } }) console.log(newObj.name) console.log(newObj.ages)

例子:创建标签

var proxy = new Proxy({},{ get(target,property){ return function(attr={},...children){ const el = document.createElement(property); // 添加属性 for(let key of Object.keys(attr)){ el[key] = attr[key] } // 添加子元素 for (let child of children){ if(typeof child == 'string'){ child = document.createTextNode(child) } el.appendChild(child) } return el; } } })

3.set(){} 设置时拦截

let obj = { name: 'dou', age: 18 } let newObj = new Proxy(obj, { set(target, prop, value) { if (!Number.isInteger(value)) { throw new Error('年龄必须为整数') } } }) newObj.age = 18.6; // 报错

4. deleteProperty (){} 删除属性时 拦截

var obj = { a: 1, b: 2 } var newObj = new Proxy(obj,{ deleteProperty(target,prop){ console.log(`你要删除${prop}属性`); // TODO delete target[prop] } }) delete newObj.a

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 = {}; let {proxy, revoke} = Proxy.revocable(target, handler); proxy.foo = 123; proxy.foo // 123 revoke(); proxy.foo // TypeError: Revoked

Proxy.revocable方法返回一个对象,该对象的proxy属性是Proxy实例,revoke属性是一个函数,可以取消Proxy实例。上面代码中,当执行revoke函数之后,再访问Proxy实例,就会抛出一个错误。

Proxy.revocable的一个使用场景是,目标对象不允许直接访问,必须通过代理访问,一旦访问结束,就收回代理权,不允许再次访问。

8. Proxy支持的拦截操作

get(target, propKey, receiver):拦截对象属性的读取,比如proxy.foo和proxy['foo']。set(target, propKey, value, receiver):拦截对象属性的设置,比如proxy.foo = v或proxy['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)