proxy的理解
欢迎大家访问我的博客dreamITGirl,不要吝啬你们的小星星,点个star~ 有问题的话,你可以将问题在 GitHub问我.
Proxy
概述
-
proxy用于修改某些操作的默认行为,等同于语言层面的修改,属于一种”元编程“。
-
proxy可以理解为,在目标对象之前设置了一层拦截,外界访问这个对象时,都要先通过这层拦截。因此提供了一种机制,可以对外界的访问进行过滤和改写。像Vue中的computed属性和双向绑定原理中都是和proxy同样的原理。
-
语法
const p = new Proxy(target,handler)
-
target:要使用proxy包装的目标对象(可以是任何类型的对象,包括原生数组、函数,甚至另一个代理)
-
handler:一个通常以函数作为属性的对象,各属性的函数分别定义了在执行各种操作时代理p的行为
-
举个例子
var handler = { get:function(target,name){ console.log(target,name) return 66 } } var proxy = new Proxy({},handler) console.log(proxy.name) console.log(proxy.time) console.log(proxy.title)
var handler = { get:function(target,name){ console.log('get方法') return 77 }, set:function(target,name,value){ console.log('set方法') target[name] = value } } var proxy = new Proxy({},handler) console.log(proxy.name) console.log(proxy.time) proxy.title = '测试代码'
-
一个技巧是将 Proxy 对象,设置到object.proxy属性,从而可以在object对象上调用;Proxy 实例也可以作为其他对象的原型对象;
同一个拦截函数可以设置多个拦截操作 -
再举个例子
var handler = { get:function(target,name){ if(name == 'prototype'){ return Object.prototype } return 'Hello' + name }, apply:function(target,thisBinding,args){ console.log(thisBinding) // 被调用时的上下文对象。 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) // {value:2} fproxy.prototype === Object.prototype // true fproxy.foo === "Hello, foo" // true
-
Proxy支持的拦截操作:
-
get(target,property,receiver) : 拦截对象属性的读取
-
set(target,property,value,receiver):拦截对象属性的设置,返回一个布尔值
-
has(target,property): 拦截
property in proxy
的操作,返回一个布尔值 -
ownKeys(target):拦截
Object.getOwnPropertyNames(proxy)、Object.getOwnPropertySymbols(proxy)、Object.keys(proxy)、for...in
循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而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)
。
-
实例方法
-
get()
该方法会拦截目标对象的以下操作- 访问属性:
proxy[foo]和proxy.bar
- 访问原型链上的属性:
Object.create(proxy)[foo]
reflect.get()
以下情况,proxy会抛出
TypeError
- 如果访问的目标属性是不可写以及不可配置的,则返回的值必须与该目标属性的值相同
- 如果要访问的目标属性没有配置访问的方法,即get方法是undefined,则返回值必须是undefined
举个例子
var person = { name:'Alice' }, handler = { get:function(target,propkey){ if(propkey in target){ return target[propkey] } else { throw new ReferenceError(`prop name ${propkey} does not exist.`) } } } var proxy = new Proxy(person,handler) console.log(proxy.name) // Alice console.log(proxy.age) // ReferenceError get方法可以继承
let proto = new Proxy({},{ get:function(target,name,receiver){ console.log('get'+name) return target[name] } }) let obj = Object.create(proto) console.log(obj.foo) // getfoo - 访问属性:
-
set()
该方法会拦截目标对象的以下操作- 指定属性值
proxy[foo] = bar
和proxy.foo = bar
- 指定继承者的属性值:
Object.create(proxy)[foo] = bar
- reflect.set()
以下的情况,proxy 会抛出一个 TypeError 异常:
- 若目标属性是一个不可写及不可配置的数据属性,则不能改变它的值。
- 如果目标属性没有配置存储方法,即 [[Set]] 属性的是 undefined,则不能设置它的值。
- 在严格模式下,如果 set() 方法返回 false,那么也会抛出一个 TypeError 异常
举个例子
let p = new Proxy({},{ set:function(target,prop,value,receiver){ target[prop] = value console.log(`property set ${prop} = ${value}`) return true } }) console.log('name' in p) // false p.name = 10 console.log('name' in p) // true console.log(p.name) // 10 const handler = { set:function(target,prop,value,receiver){ target[prop] = receiver } } const proxy = new Proxy({},handler) proxy.foo = 'bar' proxy,foo === proxy set
方法的第四个参数receiver
,一般情况下proxy
实例本身const handler = { set: function(obj, prop, value, receiver) { obj[prop] = receiver; } }; const proxy = new Proxy({}, handler); const myObj = {}; Object.setPrototypeOf(myObj, proxy); myObj.foo = 'bar'; myObj.foo === myObj - 指定属性值
-
apply()
该方法会拦截目标对象的以下操作:- proxy(...args)
Function.prototype.apply()
和Function.prototype.call()
Reflect.apply()
以下情况,代理将抛出一个TypeError:
- target必须是可被调用的。也就是说,它必须是一个函数对象。
举个例子
var p = new Proxy(function() {}, { apply: function(target, thisArg, argumentsList) { console.log('called: ' + argumentsList.join(', ')); return argumentsList[0] + argumentsList[1] + argumentsList[2]; } }); console.log(p(1,2,3)) -
construct()
该方法用于拦截new操作符,为了使new
操作符在生成的proxy对象上生效,用于初始化代理的目标对象自身必须具有[[Construct
]]内部方法(也就是说new target
必须是有效的)举个例子
var p = new Proxy(function(){},{ construct:function(target,args,newTarget){ console.log(`called ${args.join(',')}`) return {value:args[0] * 10} } }) console.log(new p(1).value)
this问题
虽然 Proxy 可以代理针对目标对象的访问,但它不是目标对象的透明代理,即不做任何拦截的情况下,也无法保证与目标对象的行为一致。主要原因就是在 Proxy 代理的情况下,目标对象内部的this关键字会指向 Proxy 代理。
const target = { m: function () { console.log(this === proxy); } }; const handler = {}; const proxy = new Proxy(target, handler); target.m() // false proxy.m() // true
上面的代码中,一旦proxy代理了target,targot内部的this的指向就会指向proxy,
本文来自博客园,作者:前端加油站,转载请注明原文链接:https://www.cnblogs.com/bllx/p/15927851.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构