• 优雅而高效的JavaScript——Proxy 和 Reflect


    在这里插入图片描述
    🤔博主:小猫娃来啦
    🤔文章核心:优雅而高效的JavaScript——Proxy 和 Reflect

    Proxy 和 Reflect是什么

    Proxy 和 Reflect 是两个强大的功能,它们可以为我们提供了更大的灵活性和控制力,以创建自定义行为的对象代理和实现元编程的功能。Proxy 是用于创建对象代理的特性,它可以拦截并操作对象的底层操作。Reflect 是一个内置对象,提供了一组静态方法,用于执行与 Proxy 相关的默认行为。


    Proxy

    创建 Proxy 对象

    要创建一个 Proxy 对象,我们使用 Proxy 构造函数,并传入两个参数:目标对象和一个处理程序对象。目标对象是被代理的对象,处理程序对象定义了在拦截器方法中实现的自定义行为。

    const target = {}; // 目标对象
    const handler = {}; // 处理程序对象
    const proxy = new Proxy(target, handler);
    
    • 1
    • 2
    • 3

    拦截器方法

    在处理程序对象中,我们可以定义一组拦截器方法。每个拦截器方法对应一个底层操作,当执行底层操作时,拦截器方法将会被触发。

    以下是一些常见的拦截器方法:

    • get(target, property, receiver): 拦截对象的属性读取操作。
    • set(target, property, value, receiver): 拦截对象的属性写入操作。
    • apply(target, thisArg, argumentsList): 拦截函数的调用操作。
    • construct(target, argumentsList, newTarget): 拦截类的实例化操作。

    拦截器示例:属性拦截

    让我们通过一个示例来理解属性拦截。

    const target = {
      name: 'John',
      age: 30
    };
    
    const handler = {
      get(target, property, receiver) {
        console.log(`读取属性:${property}`);
        return target[property];
      },
    
      set(target, property, value, receiver) {
        console.log(`设置属性:${property} = ${value}`);
        target[property] = value;
        return true;
      }
    };
    
    const proxy = new Proxy(target, handler);
    
    console.log(proxy.name); // 读取属性:name,输出:John
    
    proxy.age = 35; // 设置属性:age = 35
    console.log(proxy.age); // 读取属性:age,输出:35
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在上面的代码中,我们创建了一个目标对象 target,并定义了一个处理程序对象 handler,其中的 get 和 set 方法分别用于拦截属性的读取和写入操作。通过创建 Proxy 对象 proxy,我们可以访问目标对象的属性,并在每个操作上触发拦截器方法。

    拦截器示例:方法拦截

    除了属性拦截,我们还可以使用拦截器方法拦截函数的调用操作。让我们看一个例子:

    const target = {
      sum(x, y) {
        return x + y;
      }
    };
    
    const handler = {
      apply(target, thisArg, argumentsList) {
        console.log('调用 sum 方法');
        return target.sum(...argumentsList);
      }
    };
    
    const proxy = new Proxy(target, handler);
    
    console.log(proxy.sum(2, 3)); // 调用 sum 方法,输出:5
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在上面的代码中,我们定义了一个目标对象 target,其中的 sum 方法接受两个参数并返回它们的和。使用 apply 拦截器方法,我们可以在每次调用 sum 方法时触发一些自定义行为。


    Reflect

    Reflect 的静态方法

    Reflect 是一个内置对象,提供了一组静态方法,用于执行与 Proxy 相关的默认行为。这些方法与拦截器方法相对应,它们提供了一种简单的方式来调用默认行为,而不是完全重写拦截器方法。

    以下是一些 Reflect 的静态方法:

    • Reflect.get(target, property, receiver): 访问指定对象的属性。
    • Reflect.set(target, property, value, receiver): 设置指定对象的属性。
    • Reflect.apply(target, thisArg, argumentsList): 调用指定的函数。
    • Reflect.construct(target, argumentsList, newTarget): 创建指定类的实例。

    Reflect 示例:拦截对象操作

    让我们通过一个示例来理解如何使用 Reflect 的方法来拦截对象操作。

    const target = {
      name: 'John',
      age: 30
    };
    
    const handler = {
      get(target, property, receiver) {
        console.log(`读取属性:${property}`);
        return Reflect.get(target, property, receiver);
      },
    
      set(target, property, value, receiver) {
        console.log(`设置属性:${property} = ${value}`);
        return Reflect.set(target, property, value, receiver);
      }
    };
    
    const proxy = new Proxy(target, handler);
    
    console.log(proxy.name); // 读取属性:name,输出:John
    
    proxy.age = 35; // 设置属性:age = 35
    console.log(proxy.age); // 读取属性:age,输出:35
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在上面的代码中,我们使用 Reflect 的 get 和 set 方法在拦截器方法中调用默认行为。通过使用 Reflect,我们可以避免完全重写拦截器方法,而只关注需要自定义的行为。

    Reflect 示例:操作原型链

    除了拦截对象属性的读写操作,Reflect 还提供了一些方法来操作原型链。让我们看一个例子:

    class Person {
      constructor(name) {
        this.name = name;
      }
    }
    
    const handler = {
      has(target, property) {
        console.log(`检查属性:${property}`);
        return Reflect.has(target, property);
      },
    
      get(target, property, receiver) {
        console.log(`读取属性:${property}`);
        return Reflect.get(target, property, receiver);
      }
    };
    
    const proxy = new Proxy(Person, handler);
    
    console.log('name' in proxy); // 检查属性:name,输出:true
    
    const john = new proxy('John');
    console.log(john.name); // 读取属性:name,输出:John
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    在上面的代码中,我们创建了一个 Person 类,并使用 has 和 get 方法拦截了原型链操作。通过创建 Proxy 对象 proxy,并将 Person 类传递给它,我们可以在每次操作原型链时触发自定义行为。


    Proxy 和 Reflect 的应用

    对象代理

    通过使用 Proxy 和 Reflect,我们可以创建对象代理来拦截和加工对象的底层操作。

    例如,我们可以使用 Proxy 来创建一个简单的缓存代理:

    const cache = new Map();
    
    const handler = {
      get(target, property, receiver) {
        if (cache.has(property)) {
          console.log(`从缓存中读取属性:${property}`);
          return cache.get(property);
        }
    
        const value = Reflect.get(target, property, receiver);
        cache.set(property, value);
        console.log(`将属性缓存:${property}`);
        return value;
      },
    
      set(target, property, value, receiver) {
        console.log(`设置属性:${property} = ${value}`);
        cache.set(property, value);
        return Reflect.set(target, property, value, receiver);
      }
    };
    
    const obj = new Proxy({}, handler);
    
    obj.name = 'John'; // 设置属性:name = John
    console.log(obj.name); // 从缓存中读取属性:name,输出:John
    
    obj.name = 'Jane'; // 设置属性:name = Jane
    console.log(obj.name); // 从缓存中读取属性:name,输出:Jane
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在上面的代码中,我们创建了一个对象代理,它使用一个 Map 缓存对象的属性。在拦截器方法中,我们首先检查缓存中是否存在属性值,如果有,我们直接从缓存中读取。否则,我们使用 Reflect.get 方法获取属性值,并将其存储到缓存中。

    元编程

    元编程是指编写能够操作自身行为的代码。

    通过使用 Proxy 和 Reflect,我们可以实现一些元编程的功能,例如动态属性访问、属性校验、方法调用等。

    让我们看一个示例,使用 Proxy 实现动态属性访问:

    const person = {
      name: 'John',
      age: 30
    };
    
    const handler = {
      get(target, property) {
        if (!(property in target)) {
          throw new Error(`属性不存在:${property}`);
        }
    
        return Reflect.get(target, property);
      }
    };
    
    const proxy = new Proxy(person, handler);
    
    console.log(proxy.name); // John
    console.log(proxy.age); // 30
    console.log(proxy.city); // 抛出错误:属性不存在:city
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    在上面的代码中,我们使用 Proxy 实现动态属性访问。在处理程序对象的 get 方法中,我们首先检查属性是否存在于目标对象中。如果不存在,我们抛出一个错误。否则,我们使用 Reflect.get 方法获取属性值。


    总结

    Proxy 和 Reflect 是 JavaScript 中强大的特性,它们为我们提供了更大的灵活性和控制力来创建自定义行为的对象代理和实现元编程的功能。在本文中,我们学习了 Proxy 和 Reflect 的基本概念,介绍了它们的使用方法和示例。我们还探讨了 Proxy 和 Reflect 的应用领域,包括对象代理和元编程。希望通过本文的学习,你对 Proxy 和 Reflect 的概念和用法有了更深入的理解。

    在这里插入图片描述


  • 相关阅读:
    细胞膜修饰脂质体/蛋白/聚合物纳米载体/纳米胶束/磁性纳米粒子
    SpringBoot 使用 Sa-Token 实现账号封禁、分类封禁、阶梯封禁
    初学Matlab第一天
    【无标题】
    SpringBoot2.7升级到3.0的实践分享
    Python基础学习笔记1(AI Studio)
    git代码管理 分支相关 新建dev、hot分支,分支协同操作
    Day5:写前端项目(html+css+js)-导航部分p1
    大数据之FlinkCDC
    Memory Management in Rust
  • 原文地址:https://blog.csdn.net/dyk11111/article/details/133877571