Skip to content

Proxy

概念

在JavaScript中,Proxy是一个特殊的“包装器”对象,它可以用于修改或扩展某些基本操作的行为,比如属性读取、函数调用等。这种修改或扩展的行为是通过所谓的"traps"实现的,这些"traps"定义了如何拦截和改变基本操作。

javascript
let target = {
  name: "target"
};

let proxy = new Proxy(target, {
  get: function(target, property) {
    return property in target ? target[property] : "Default";
  }
});

console.log(proxy.name); // 输出 "target"
console.log(proxy.unknown); // 输出 "Default"

在上面的例子中,当我们尝试从proxy读取不存在的属性时,我们得到了"default",而不是通常的"undefined"。这是因为我们的"get" trap拦截了读取操作,并返回了默认值。

用途

Proxy有许多用途,下面是一些常见的例子:

数据校验

Proxy可以用于校验设置对象属性的值:

javascript
let validator = {
  set: function(target, property, value) {
    if (property === "age") {
      if (!Number.isInteger(value)) {
        throw new TypeError("The age is not an integer");
      }
      if (value < 0 || value > 200) {
        throw new RangeError("The age is invalid");
      }
    }

    target[property] = value;
    return true;
  }
};

let person = new Proxy({}, validator);

person.age = 100; // 正常
console.log(person.age); // 输出 100
person.age = "young"; // 抛出 TypeError: The age is not an integer
person.age = 300; // 抛出 RangeError: The age is invalid

数据绑定和观察

Proxy可以用于实现数据绑定和观察(数据变化的监听):

javascript
let handler = {
  set: function(target, property, value) {
    console.log(`${property} is set to ${value}`);
    target[property] = value;
    return true;
  }
};

let proxy = new Proxy({}, handler);

proxy.name = "proxy"; // 输出 "name is set to proxy"

函数参数的默认值

Proxy可以用于给函数参数设置默认值:

javascript
function defaultValues(target, defaults) {
  return new Proxy(target, {
    apply: function(target, thisArg, args) {
      args = args.map((arg, index) => arg === undefined ? defaults[index] : arg);
     

 return target.apply(thisArg, args);
    }
  });
}

let add = defaultValues(function(x, y) {
  return x + y;
}, [0, 0]);

console.log(add(1, 1)); // 输出 2
console.log(add(undefined, 1)); // 输出 1
console.log(add(1)); // 输出 1

以上仅仅是Proxy能做的事情的一部分。在实际开发中,你可以根据需要灵活使用Proxy。

Proxy vs Reflect

在ES6中引入了另一个新的全局对象Reflect,它提供了一组用于执行JavaScript基本操作的方法,例如Reflect.get()Reflect.set()等。这些方法与Proxy的traps一一对应。这使得Proxy的traps可以使用对应的Reflect方法来执行被拦截的操作:

javascript
let proxy = new Proxy(target, {
  get: function(target, property) {
    return Reflect.get(target, property);
  }
});

Reflect的方法有许多优点。首先,它们总是返回一个期望的值,使得代码更易于理解和调试。其次,它们提供了一种正确处理JavaScript基本操作的方法。例如,使用Reflect.set()可以正确处理设置只读属性的情况。