ES中Proxy 和 Reflect的用法和示例

一、Proxy 详解

Proxy 用于创建一个对象的代理,从而实现对对象操作(如读取、赋值、删除等)的拦截和自定义处理。

核心语法
javascript 复制代码
const proxy = new Proxy(target, handler);
  • target:被代理的目标对象(可以是任意类型对象,包括数组、函数、甚至另一个 Proxy)。
  • handler:拦截配置对象,包含多个拦截方法(如 getsetdeleteProperty 等)。
常用拦截方法 & 示例
1. 拦截属性读取(get

场景:给不存在的属性返回默认值,或禁止访问私有属性(以 _ 开头)。

javascript 复制代码
const user = { name: "张三", age: 20, _id: "123456" };

const userProxy = new Proxy(user, {
  get(target, prop) {
    // 禁止访问私有属性
    if (prop.startsWith("_")) {
      throw new Error(`禁止访问私有属性 ${prop}`);
    }
    // 不存在的属性返回默认值
    return target[prop] ?? "默认值";
  },
});

console.log(userProxy.name); // 张三
console.log(userProxy.gender); // 默认值
console.log(userProxy._id); // 抛出错误:禁止访问私有属性 _id
2. 拦截属性赋值(set

场景:校验属性赋值的合法性(如年龄必须是数字且范围合理)。

javascript 复制代码
const user = { name: "张三", age: 20 };

const userProxy = new Proxy(user, {
  set(target, prop, value) {
    if (prop === "age") {
      if (typeof value !== "number") {
        throw new Error("年龄必须是数字");
      }
      if (value < 0 || value > 150) {
        throw new Error("年龄范围必须在 0-150 之间");
      }
    }
    // 合法则执行赋值
    target[prop] = value;
    // 必须返回 true 表示赋值成功(严格模式下不返回会报错)
    return true;
  },
});

userProxy.age = 25;
console.log(userProxy.age); // 25

userProxy.age = "三十"; // 抛出错误:年龄必须是数字
userProxy.age = 200; // 抛出错误:年龄范围必须在 0-150 之间
3. 拦截属性删除(deleteProperty

场景:禁止删除核心属性。

javascript 复制代码
const user = { name: "张三", age: 20 };

const userProxy = new Proxy(user, {
  deleteProperty(target, prop) {
    if (prop === "name") {
      throw new Error("核心属性 name 禁止删除");
    }
    delete target[prop];
    return true;
  },
});

delete userProxy.age;
console.log(userProxy.age); // undefined

delete userProxy.name; // 抛出错误:核心属性 name 禁止删除
4. 拦截函数调用(apply

场景:增强函数功能(如统计调用次数)。

javascript 复制代码
function add(a, b) {
  return a + b;
}

const addProxy = new Proxy(add, {
  apply(target, thisArg, args) {
    console.log(`函数被调用,参数:${args}`);
    // 增强功能:参数转数字后计算
    const newArgs = args.map((item) => Number(item));
    return target(...newArgs);
  },
});

console.log(addProxy("10", 20)); // 输出:函数被调用,参数:10,20 → 30

二、Reflect 详解

Reflect 是 ES6 新增的内置对象,提供了一组统一的方法来操作对象(对应 Object 的操作方法,如 Reflect.get 对应 obj[prop]),且所有方法都是静态的。

核心特点
  1. 方法与 Proxy 的拦截方法一一对应(如 Reflect.get 对应 Proxyget 拦截);
  2. 操作对象的方法返回布尔值表示操作是否成功(如 Reflect.set 返回 true/false);
  3. 避免 Object 方法的怪异行为(如 Object.defineProperty 失败抛错,Reflect.defineProperty 返回布尔值)。
常用方法 & 示例
1. 读取/赋值属性(Reflect.get/Reflect.set
javascript 复制代码
const user = { name: "张三", age: 20 };

// 读取属性
console.log(Reflect.get(user, "name")); // 张三
console.log(Reflect.get(user, "gender", "未知")); // 未知(第三个参数为默认值)

// 赋值属性
const setSuccess = Reflect.set(user, "age", 25);
console.log(setSuccess); // true
console.log(user.age); // 25

// 赋值失败(不可写属性)
const readonlyUser = Object.freeze({ name: "李四" });
console.log(Reflect.set(readonlyUser, "name", "王五")); // false
2. 删除属性(Reflect.deleteProperty
javascript 复制代码
const user = { name: "张三", age: 20 };

// 删除成功
console.log(Reflect.deleteProperty(user, "age")); // true
console.log(user.age); // undefined

// 删除失败(不可配置属性)
const nonConfigUser = {};
Object.defineProperty(nonConfigUser, "id", {
  value: 123,
  configurable: false,
});
console.log(Reflect.deleteProperty(nonConfigUser, "id")); // false
3. 定义属性(Reflect.defineProperty
javascript 复制代码
const user = {};

// 定义属性成功
const defineSuccess = Reflect.defineProperty(user, "name", {
  value: "张三",
  writable: true,
});
console.log(defineSuccess); // true
console.log(user.name); // 张三

// 定义属性失败(重复定义不可配置属性)
Reflect.defineProperty(user, "name", {
  value: "李四",
  configurable: false,
});
console.log(Reflect.defineProperty(user, "name", { value: "王五" })); // false

三、Proxy + Reflect 结合使用

Reflect 常配合 Proxy 使用,让拦截逻辑更规范(替代直接操作 target),且能自动处理 this 绑定问题。

示例:通用的对象校验代理
javascript 复制代码
const validator = {
  set(target, prop, value) {
    // 用 Reflect.set 替代 target[prop] = value,返回布尔值更规范
    const success = Reflect.set(target, prop, value);
    // 自定义校验逻辑
    if (prop === "age" && (value < 0 || value > 150)) {
      console.warn("年龄范围不合法");
      // 校验失败则回滚
      Reflect.set(target, prop, target[prop]);
      return false;
    }
    return success;
  },
  get(target, prop) {
    // 用 Reflect.get 读取,支持默认值
    return Reflect.get(target, prop, "默认值");
  },
};

const user = new Proxy({ name: "张三", age: 20 }, validator);

user.age = 200; // 输出:年龄范围不合法
console.log(user.age); // 20(回滚为原值)
console.log(user.gender); // 默认值(Reflect.get 的默认值)

四、核心总结

特性 Proxy Reflect
作用 拦截对象操作,自定义行为 提供标准化的对象操作方法
调用方式 实例化后通过代理对象操作 静态方法直接调用
返回值 代理对象 操作结果(布尔值/属性值等)
典型场景 数据校验、权限控制、函数增强 配合 Proxy 简化拦截逻辑、安全操作对象

Proxy 是"拦截层",Reflect 是"标准化操作层",两者结合能优雅实现对象的自定义行为,是 ES6 中处理对象操作的最佳实践。