JS中的代理与反射 — Proxy和Reflect

简介: 一提到ES6,相信大家不会陌生。自2015年正式发布至今一直备受追捧。它所带来的很多特性提高了日常开发效率,改变了构建代码的思维方式。作为一个规范,起到了推动语言发展的作用。 最近在和同组同学聊天的时候,偶然得知他们准备使用proxy来解决目前框架的一些不足。突然发现Proxy是一个不太容易被人提起的东西。在好多ES6语法用的非常熟的情况下,依然会有一些东西被忽略掉。例如接下来要提到的Pro

一提到ES6,相信大家不会陌生。自2015年正式发布至今一直备受追捧。它所带来的很多特性提高了日常开发效率,改变了构建代码的思维方式。作为一个规范,起到了推动语言发展的作用。

最近在和同组同学聊天的时候,偶然得知他们准备使用proxy来解决目前框架的一些不足。突然发现Proxy是一个不太容易被人提起的东西。在好多ES6语法用的非常熟的情况下,依然会有一些东西被忽略掉。例如接下来要提到的Proxy和Reflect。

Proxy和Reflect是什么

Proxy正常翻译做代理,可以作用与对象或者函数上,数据ES6的新特性之一,用以实现js的元编程

const proxy = new Proxy();

使用Proxy,可以对函数或者对象的一些操作进行“代理”,或者这个地方叫做拦截会更合适一些。也就是说,使用代理,可以拦截对象或者函数的一些操作,这些操作包括但不限于:读、写、in等等操作。

有一个使用的场景:如何能够打日志记录下来某一个类的实例被读取过什么字段以及什么字段被赋值过?

我们先来看Proxy的构造函数

<T extends object>(target: T, handler: ProxyHandler<T>): T

从函数定义可以看出,Proxy在构建的时候,需要传入被代理的对象target以及一个handler。

被代理的对象可以是一个空对象{},那handler是什么呢?

handler实际上是一个对象,其记录了需要代理的行为,以及代理的方法。一个典型的handler定义如下

const handler = {
  get(target, propKey, receiver) {
    console.log('GET '+ propKey);
       // do other thing...
  },
  set(target, propKey, value, receiver) {
       console.log('SET ' + propKey);
       // do other thing...
   },
}

使用这个代理就能够在对象被读取或者被写入的情况下,打印出具体被操作的key。

接下来我们做一个实验。假设我想把上面handler的规则,运用到某个对象上。

const obj = {};
const objP = new Proxy(obj, handler);
objP.hello = 'hello';

控制台会打印出下面的内容

SET hello
"hello"

然而假设这个时候,你直接在控制台中输入objP.hello,并期望得到objP.hello的值hello的时候,你得到的只有

GET hello
undefined

而并没有"hello"。这是因为上面的handler对get和set进行了拦截,并没有去执行真正的get、set的代码。所以变量没有被赋值。而这个时候就需要Reflect出场了。

Reflect是ES6的另一个新特性,主要用户对象上,实现了JS的反射

看到这里,会JAVA的同学要开心了:反射?我熟啊!!

恩,这里的反射和JAVA的理解差不多。可以把对象的结构及其他相关信息“反射出来”。通过这个新对象,可以方便的获取到对象的结构、props、参数函数等等,并且可以以更优雅的方式来调用props里的方法而避免一些问题的发生。

回到上面那个例子,需要把handler给改一下:

const handler = {
  get(target, propKey, receiver) {
    console.log('GET '+ propKey);
       // do other thing...
    return Reflect.get(target, propKey, receiver);
  },
  set(target, propKey, value, receiver) {
       console.log('SET ' + propKey);
       // do other thing...
    return Reflect.set(target, propKey, value, receiver);
   },
}

与上面例子不同的是,在get和set的最后,加上了Reflect.get、Reflect.set,使用这样的方式,能够让get、set继续执行它之前应该执行的行为。

再执行一遍上面的实例,最后在敲入objP.hello的时候,就能得到想要的结果。

GET hello
"hello"

使用场景

说了这么多用法,举几个可以使用的场景。

1 通过对set、get函数的代理,我们可以对访问不存在的对象属性进行单独的处理。

  get(target, propKey, receiver) {
    console.log('GET '+ propKey);
    if(!(propKey in target)){
      throw new Exception('null target');
    }
    return Reflect.get(target, propKey, receiver);
  },

2 MVVM的数据双绑。

function observedArray(cb) {
    const array = [];
    return new Proxy(array, {
        set(target, propertyKey, value, receiver) {
            cb(propertyKey, value);
            return Reflect.set(target, propertyKey, value, receiver);
        }
    });    
}
const array = observedArray(
    (key, value) => console.log(`${key}: ${value}`));

array.push('a');

这个时候当array数组元素改变的时候,会调用对应的回调。

3 做Type checking

Proxy支持的代理方法列表

以下列表摘选自EXPLORING ES6

对象类:

  •   defineProperty(target, propKey, propDesc) : boolean
  •   deleteProperty(target, propKey) : boolean
  •   get(target, propKey, receiver) : any
  •   getOwnPropertyDescriptor(target, propKey) : PropDesc|Undefined
  •   getPrototypeOf(target) : Object|Null
  •   has(target, propKey) : boolean
  •   isExtensible(target) : boolean
  •   ownKeys(target) : Array<PropertyKey>
  •   preventExtensions(target) : boolean
  •   set(target, propKey, value, receiver) : boolean
  •   setPrototypeOf(target, proto) : boolean

    函数类

  •   apply(target, thisArgument, argumentsList) : any
相关文章
|
6月前
|
缓存 JavaScript 数据安全/隐私保护
js开发:请解释什么是ES6的Proxy,以及它的用途。
`ES6`的`Proxy`对象用于创建一个代理,能拦截并自定义目标对象的访问和操作,应用于数据绑定、访问控制、函数调用的拦截与修改以及异步操作处理。
74 3
|
6月前
|
API
在vite.config.js 配置代理
在vite.config.js 配置代理
686 2
|
6月前
|
JavaScript 前端开发 定位技术
JavaScript 中如何代理 Set(集合) 和 Map(映射)
JavaScript 中如何代理 Set(集合) 和 Map(映射)
113 0
|
6月前
|
JSON JavaScript 前端开发
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)(下)
Webpack【Webpack图片处理、Webpack中proxy代理 、自动清理dist、Webpack优化、JavaScript中的代码检查】(三)-全面详解(学习总结---从入门到深化)
89 2
|
3月前
|
编解码 JavaScript 前端开发
JS逆向浏览器脱环境专题:事件学习和编写、DOM和BOM结构、指纹验证排查、代理自吐环境通杀环境检测、脱环境框架、脱环境插件解决
JS逆向浏览器脱环境专题:事件学习和编写、DOM和BOM结构、指纹验证排查、代理自吐环境通杀环境检测、脱环境框架、脱环境插件解决
113 1
|
3月前
|
JavaScript 前端开发 开发者
Vue.js 响应式变革来袭!结合热点技术,探索从 Object.defineProperty 到 Proxy 的奇妙之旅,触动你的心
【8月更文挑战第30天】在 Vue.js 中,响应式系统自动追踪并更新数据变化,极大提升了开发体验。早期通过 `Object.defineProperty` 实现,但存在对新旧属性处理及数组操作的局限。Vue 3.0 引入了 `Proxy`,克服了上述限制,提供了更强大的功能和更好的性能。实践中,可根据项目需求选择合适的技术方案,并优化数据操作,利用懒加载等方式提升性能。
43 0
|
4月前
|
前端开发 JavaScript Linux
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
若依修改之后,无法访问前端项目如何解决,只能访问后端的接口,我的接口8083,端不显示咋解决?在vue.config.js文件中的映射路径要跟后端匹配,到软件商店里找到Ngnix配置代理,设80不用加
|
4月前
|
设计模式 JavaScript 前端开发
精读JavaScript中的代理(Proxy)与反射(Reflect)
精读JavaScript中的代理(Proxy)与反射(Reflect)
43 0
|
6月前
|
JSON JavaScript 前端开发
vue2_vite.config.js的proxy跨域配置和nginx配置代理有啥区别?
vue2_vite.config.js的proxy跨域配置和nginx配置代理有啥区别?
223 1
|
6月前
|
前端开发 JavaScript API
前端 JS 经典:Proxy 和 DefineProperty
前端 JS 经典:Proxy 和 DefineProperty
99 0