HLJ 发布于
2025-06-11 11:33:49
0阅读

JavaScript Proxy 和 Reflect 详解

JavaScript Proxy 和 Reflect

Proxy(代理)

Proxy 是 ES6 引入的一个强大特性,它允许你创建一个对象的代理,可以拦截和自定义对象的基本操作。

基本语法

const proxy = new Proxy(target, handler);
  • target:要代理的目标对象
  • handler:包含拦截操作(traps)的对象

常用拦截操作(Traps)

const handler = {
  // 拦截属性读取
  get(target, prop, receiver) {
    console.log(`Getting ${prop}`);
    return Reflect.get(...arguments);
  },
  
  // 拦截属性设置
  set(target, prop, value, receiver) {
    console.log(`Setting ${prop} to ${value}`);
    return Reflect.set(...arguments);
  },
  
  // 拦截 in 操作符
  has(target, prop) {
    console.log(`Checking if ${prop} exists`);
    return Reflect.has(...arguments);
  },
  
  // 拦截 delete 操作
  deleteProperty(target, prop) {
    console.log(`Deleting ${prop}`);
    return Reflect.deleteProperty(...arguments);
  },
  
  // 拦截函数调用
  apply(target, thisArg, argumentsList) {
    console.log('Function called');
    return Reflect.apply(...arguments);
  },
  
  // 拦截 new 操作符
  construct(target, argumentsList, newTarget) {
    console.log('Constructor called');
    return Reflect.construct(...arguments);
  }
};

使用示例

const target = {
  name: 'John',
  age: 30
};

const proxy = new Proxy(target, {
  get(target, prop) {
    if (prop in target) {
      return target[prop];
    } else {
      return `Property "${prop}" does not exist`;
    }
  },
  set(target, prop, value) {
    if (prop === 'age' && typeof value !== 'number') {
      throw new TypeError('Age must be a number');
    }
    target[prop] = value;
    return true; // 表示设置成功
  }
});

console.log(proxy.name); // "John"
console.log(proxy.unknown); // "Property "unknown" does not exist"
proxy.age = 31; // OK
proxy.age = 'thirty'; // TypeError: Age must be a number

Reflect(反射)

Reflect 是一个内置对象,它提供了拦截 JavaScript 操作的方法。这些方法与 Proxy handler 的方法一一对应。

Reflect 的主要方法

Reflect.get(target, propertyKey[, receiver])
Reflect.set(target, propertyKey, value[, receiver])
Reflect.has(target, propertyKey)
Reflect.deleteProperty(target, propertyKey)
Reflect.apply(target, thisArgument, argumentsList)
Reflect.construct(target, argumentsList[, newTarget])
Reflect.ownKeys(target)
Reflect.defineProperty(target, propertyKey, attributes)
Reflect.getOwnPropertyDescriptor(target, propertyKey)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, prototype)
Reflect.isExtensible(target)
Reflect.preventExtensions(target)

Reflect 的优势

  1. 更一致的 API:与直接操作对象相比,Reflect 提供了更一致的 API
  2. 返回值更合理:例如 Reflect.defineProperty() 返回布尔值表示是否成功,而 Object.defineProperty() 返回对象或抛出错误
  3. 函数式编程:更适合函数式编程风格
  4. 与 Proxy 配合:Reflect 方法与 Proxy trap 一一对应,便于在 trap 中实现默认行为

使用示例

const obj = { x: 1, y: 2 };

// 使用 Reflect 读取属性
console.log(Reflect.get(obj, 'x')); // 1

// 使用 Reflect 设置属性
Reflect.set(obj, 'z', 3);
console.log(obj.z); // 3

// 检查属性是否存在
console.log(Reflect.has(obj, 'y')); // true

// 删除属性
Reflect.deleteProperty(obj, 'y');
console.log('y' in obj); // false

Proxy 和 Reflect 结合使用

Proxy 和 Reflect 经常一起使用,在 Proxy 的 trap 中调用 Reflect 的对应方法来实现默认行为:

const target = { /* ... */ };

const proxy = new Proxy(target, {
  get(target, prop, receiver) {
    console.log(`Getting ${prop}`);
    // 使用 Reflect 实现默认的 get 行为
    return Reflect.get(target, prop, receiver);
  },
  
  set(target, prop, value, receiver) {
    console.log(`Setting ${prop} to ${value}`);
    // 使用 Reflect 实现默认的 set 行为
    return Reflect.set(target, prop, value, receiver);
  }
});

这种模式让你可以在保持对象默认行为的同时添加自定义逻辑。

实际应用场景

  1. 数据验证:在设置属性时验证数据
  2. 日志记录:跟踪对象属性的访问和修改
  3. 性能测量:测量函数执行时间
  4. 自动填充:访问不存在的属性时返回默认值
  5. 私有属性:实现类似私有变量的效果
  6. 观察者模式:在属性变化时通知观察者
  7. 负索引数组:实现类似 Python 的负索引

示例:观察者模式

function createObservable(target, observer) {
  return new Proxy(target, {
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      if (result && oldValue !== value) {
        observer(key, oldValue, value);
      }
      return result;
    }
  });
}

const person = { name: 'Alice', age: 25 };
const observablePerson = createObservable(person, (key, oldValue, newValue) => {
  console.log(`Property ${key} changed from ${oldValue} to ${newValue}`);
});

observablePerson.name = 'Bob'; // 控制台输出: Property name changed from Alice to Bob
observablePerson.age = 30;    // 控制台输出: Property age changed from 25 to 30

Proxy 和 Reflect 为 JavaScript 提供了强大的元编程能力,使得开发者能够更灵活地控制对象行为。

当前文章内容为原创转载请注明出处:http://www.good1230.com/detail/2025-06-11/841.html
最后生成于 2025-06-13 16:39:14
此内容有帮助 ?
0