This article brings you relevant knowledge about Proxy and Reflect in JavaScript. I hope it will be helpful to you.
ECMAScript has added two new features, Proxy and Reflect, to the ES6 specification. These two new features enhance the controllability of object access in JavaScript, making JS modules, Class encapsulation can be more rigorous and simple, and it can also make error reporting when operating objects more controllable.
Proxy, as its name suggests, proxy. This interface can create a proxy object for the specified object. Any operations on the proxy object, such as accessing properties, assigning values to properties, and function calls, will be intercepted and then handed over to the functions we define to handle the corresponding operations.
The characteristics of JavaScript allow objects to have a lot of room for manipulation. At the same time, JavaScript also provides many methods for us to transform objects. We can add attributes at will, delete attributes at will, and change the prototype of the object at will... But the API previously provided by the Object class has Many disadvantages:
The emergence of the Proxy interface solves these problems very well:
The Proxy interface is a constructor in the JS environment:
ƒ Proxy ( target: Object, handlers: Object ) : Proxy
This constructor has two parameters. The first one is the object we want to proxy, and the second one contains Objects of functions that handle various operations.
The following is a calling example:
//需要代理的目标 var target = { msg: "I wish I was a bird!" }; //包含处理各种操作的函数的对象 var handler = { //处理其中一种操作的函数,此处是访问属性的操作 get(target, property) { //在控制台打印访问了哪个属性 console.log(`你访问了 ${property} 属性`); //实现操作的功能 return target[property]; } } //构造代理对象 var proxy = new Proxy( target , handler); //访问代理对象 proxy.msg //控制台: 你访问了 msg 属性 //← I wish I was a bird!
In the above example, an object is first created and assigned to target, and then a proxy object is created with target as the target and assigned to proxy. In the object provided to the Proxy constructor as the second parameter, there is an attribute named "get", which is a function. "get" is the name of a trap in the Proxy interface. Proxy will refer to the attribute we provide as the second parameter. For the attributes in the object, find those attributes with the same attribute name as the trap name, automatically set the corresponding trap and use the function on the attribute as the trap processing function. Traps can intercept specific operations on the proxy object, convert the details of the operation into parameters and pass them to our processing function, allowing the processing function to complete the operation, so that we can control various behaviors of the object through the processing function.
In the above example, the get function provided when constructing the proxy object is the function that handles the operation of accessing the object's properties. The proxy object intercepts the operation of accessing the object's properties and passes the target object and ## to the get function. #The attribute name requested to be accessedTwo parameters, and the return value of the function is used as the result of the access.
Trap names and corresponding function parameters | Intercepted operations | Operation examples |
---|---|---|
get(target, property) |
Access object properties |
target.property or target[property ]
|
set(target, property, value, receiver) |
Assignment object property |
target.property = value or target[property] = value
|
##has(target, property)
| Determine whether the object property existsproperty in target
|
|
Determine whether the object can add attributes |
Object.isExtensible(target) |
|
Make it impossible to add new properties to the object |
Object.preventExtensions(target) |
|
Define the properties of the object |
Object.defineProperty(target, property, descriptor) |
|
Delete the properties of the object |
delete target.property |
or delete target[property] or Object.deleteProperty(target, property)
|
Get the object Descriptor of own properties |
Object.getOwnPropertyDescriptor(target, property) |
|
Enumerate all the own properties of the object |
Object.getOwnPropertyNames(target). |
concat(Object.getOwnPropertySymbols(target))
|
Get the prototype of the object |
Object.getPrototypeOf(target) |
|
Set the prototype of the object |
Object.setPrototypeOf(target) |
|
Function call |
target(...arguments) | ortarget.apply(target, thisArg, argumentsList)
| ##construct(target, argumentsList, newTarget)
Constructor call
| new target(...arguments)
在上面列出的陷阱里是有拦截函数调用一类操作的,但是只限代理的对象是函数的情况下有效,Proxy 在真正调用我们提供的接管函数前是会进行类型检查的,所以通过代理让普通的对象拥有函数一样的功能这种事就不要想啦。 除了直接 new Proxy 对象外,Proxy 构造函数上还有一个静态函数 revocable,可以构造一个能被销毁的代理对象。 Proxy.revocable( target: Object, handlers: Object ) : Object Proxy.revocable( target, handlers ) → { proxy: Proxy, revoke: ƒ () } Copy after login 这个静态函数接收和构造函数一样的参数,不过它的返回值和构造函数稍有不同,会返回一个包含代理对象和销毁函数的对象,销毁函数不需要任何参数,我们可以随时调用销毁函数将代理对象和目标对象的代理关系断开。断开代理后,再对代理对象执行任何操作都会抛出 TypeError 错误。 //创建代理对象 var temp1 = Proxy.revocable({a:1}, {}); //← {proxy: Proxy, revoke: ƒ} //访问代理对象 temp1.proxy.a //← 1 //销毁代理对象 temp1.revoke(); //再次访问代理对象 temp1.proxy.a //未捕获的错误: TypeError: Cannot perform 'get' on a proxy that has been revoked Copy after login 弄清楚了具体的原理后,下面举例一个应用场景。 //目标对象 var target = { msg: "我是鲜嫩的美少女!", secret: "其实我是800岁的老太婆!" //不想被别人访问的属性 }; //创建代理对象 var proxy = new Proxy( target , { get(target, property) { //如果访问 secret 就报错 if (property == "secret") throw new Error("不允许访问属性 secret!"); return target[property]; } }); //访问 msg 属性 proxy.msg //← 我是鲜嫩的美少女! //访问 secret 属性 proxy.secret //未捕获的错误: 不允许访问属性 secret! Copy after login 在上面的例子中,我针对对 secret 属性的访问进行了报错,守护住了“美少女”的秘密,让我们歌颂 Proxy 的伟大! proxy//控制台: Proxy {msg: '我是鲜嫩的美少女!', secret: '其实我是800岁的老太婆!'} Copy after login 以下是关于 Proxy 的一些细节问题:
Reflect学过其他语言的人看到 Reflect 这个词可能会首先联想到“反射”这个概念,但 JavaScript 由于语言特性是不需要反射的,所以这里的 Reflect 其实和反射无关,是 JavaScript 给 Proxy 配套的一系列函数。 Reflect: Object Reflect → { apply: ƒ apply(), construct: ƒ construct(), defineProperty: ƒ defineProperty(), deleteProperty: ƒ deleteProperty(), get: ƒ (), getOwnPropertyDescriptor: ƒ getOwnPropertyDescriptor(), getPrototypeOf: ƒ getPrototypeOf(), has: ƒ has(), isExtensible: ƒ isExtensible(), ownKeys: ƒ ownKeys(), preventExtensions: ƒ preventExtensions(), set: ƒ (), setPrototypeOf: ƒ setPrototypeOf(), Symbol(Symbol.toStringTag): "Reflect" } Copy after login 可以看到,Reflect 上的所有函数都对应一个 Proxy 的陷阱。这些函数接受的参数,返回值的类型,都和 Proxy 上的别无二致,可以说 Reflect 就是 Proxy 拦截的那些操作的原本实现。 那 Reflect 存在的意义是什么呢? //需要代理的对象 var target = { get me() {return "我是鲜嫩的美少女!"} //定义 me 属性的 getter }; //创建代理对象 var proxy = new Proxy( target , { //拦截定义属性的操作 defineProperty(target, property, descriptor) { //如果定义的属性是 me 就返回 false 阻止 if (property == "me") return false; //使用 Reflect 提供的函数实现原本的功能 return Reflect.defineProperty(target, property, descriptor); } }); //尝试重新定义 me 属性 Object.defineProperty(proxy , "me", {value: "我是800岁的老太婆!"}) //未捕获的错误: TypeError: 'defineProperty' on proxy: trap returned falsish for property 'me' //尝试定义 age 属性 Object.defineProperty(proxy , "age", {value: 17}) //← Proxy {age: 17} //使用 Reflect 提供的函数来定义属性 Reflect.defineProperty(proxy , "me", {value: "我是800岁的老太婆!"}) //← false Reflect.defineProperty(proxy , "age", {value: 17}) //← true Copy after login 在上面的例子里,由于我很懒,所以我在接管定义属性功能的地方“偷工减料”用了 Reflect 提供的 defineProperty 函数。用 Object.defineProperty 在代理对象上定义 me 属性时报了错,表示失败,而定义 age 属性则成功完成了。可以看到,除了被报错的 me 属性,对其他属性的定义是可以成功完成的。我还使用 Reflect 提供的函数执行了同样的操作,可以看到 Reflect 也无法越过 Proxy 的代理,同时也显示出了 Reflect 和传统方法返回值的区别。 虽然 Reflect 的好处很多,但是它也有一个问题:JS 全局上的 Reflect 对象是可以被修改的,可以替换掉里面的方法,甚至还能把 Reflect 删掉。 //备份原本的 Reflect.get var originGet = Reflect.get; //修改 Reflect.get Reflect.get = function get(target ,property) { console.log("哈哈,你的 get 已经是我的形状了!"); return originGet(target ,property); }; //调用 Reflect.get Reflect.get({a:1}, "a") //控制台: 哈哈,你的 get 已经是我的形状了! //← 1 //删除 Reflect 变量 delete Reflect //← true //访问 Reflect 变量 Reflect //未捕获的错误: ReferenceError: Reflect is not defined Copy after login 基于上面的演示,不难想到,可以通过修改 Reflect 以欺骗的方式越过 Proxy 的代理。所以如果你对安全性有要求,建议在使用 Reflect 时,第一时间将全局上的 Reflect 深度复制到你的闭包作用域并且只使用你的备份,或者将全局上的 Reflect 冻结并锁定引用。 相关推荐:javascript学习教程 |
The above is the detailed content of Let's talk about Proxy and Reflect in Javascript. For more information, please follow other related articles on the PHP Chinese website!