ES6 Proxy 怎么理解?使用场景

66 阅读4分钟

什么是proxy

es6提供了一些新特性,其中就有Proxy构造函数,用来生成Proxy实例的,它是一种拦截器。可以理解成,在目标对象之前假设一层”拦截“,外界对该对象饿访问,都必须先通过这层拦截。Proxy提供了一些方法,可以对外界的访问进行过滤和改写。

如何使用Proxy

ES6提供了Proxy 构造函数,用来生成 Proxy 实例。

var proxy = new Proxy(target, handler);

Proxy接受两个参数。第一个参数(target)是所要代理的目标对象, 即如果没有Proxy的介入,操作原来要访问的就是这个对象;第二个参数(handler)是一个配置对象, 对于每一个被代理的操作,需要提供一个对应的处理函数,该函数将拦截对应的操作。

例子

// 创建一个普通对象
const targetObject = {
  name: "John",
  age: 30,
};
// 创建一个Proxy对象
const proxyObject = new Proxy(targetObject, {
  // 拦截读取属性的操作
  get: function (target, property) {
    console.log(`Getting value of ${property}`);
    return target[property];
  },

  // 拦截设置属性的操作
  set: function (target, property, value) {
    console.log(`Setting ${property} to ${value}`);
    target[property] = value;
  },

  // 拦截删除属性的操作
  deleteProperty: function (target, property) {
    console.log(`Deleting property ${property}`);
    delete target[property];
  },
});
// 使用Proxy对象 
console.log(proxyObject.name); // 会触发get拦截,输出 "Getting value of name"
proxyObject.age = 31; // 会触发set拦截,输出 "Setting age to 31" 
console.log(proxyObject.age); // 会触发get拦截,输出 "Getting value of age" 
delete proxyObject.age; // 会触发deleteProperty拦截,输出 "Deleting property age" console.log(proxyObject.age); // 访问被删除的属性,输出 "undefined"

Proxy 实例的一些常用方法

  • get:用于拦截对象的读取操作。当读取对象的属性时,会调用 get,并返回相应的值。
  • set:用于拦截对象的设置操作。当设置对象的属性时,会调用 set,并将相应的值传递给它。
  • apply:用于拦截函数的调用操作。当调用对象的函数时,会调用 apply,并将相应的参数传递给它。
  • construct:用于拦截 new 操作符。当使用 new 操作符创建对象的实例时,会调用 construct,并返回相应的实例。
  • has:用于拦截 in 操作符。当使用 in 操作符判断对象是否包含某个属性时,会调用 has,并返回相应的结果。
  • deleteProperty:用于拦截 delete 操作符。当使用 delete 操作符删除对象的属性时,会调用 deleteProperty,并返回相应的结果。

更多方法详情 请看ES6官网

Proxy的常用场景

1.数据验证

Proxy 对象的一个常见应用场景是数据验证。可以使用set来拦截属性的设置操作,并在设置属性之前验证属性的值是否符合某些条件。 以下是一个简单的示例,展示了如何使用set来验证数据:

let validator = {
  set: function(target, prop, value) {
    if (prop === 'age') {
      if (typeof value !== 'number' || value < 0 || value > 120) {
        throw new Error('Invalid age');
      }
    }

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

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

person.name = 'John';
person.age = 30;

console.log(person.name); // Output: John
console.log(person.age); // Output: 30

person.age = '30'; // Output: Uncaught Error: Invalid age

当属性名为'age'时,它会验证属性的值是否为数字并且是否在 0 到 120 的范围内。如果不是,它会抛出一个错误。否则,它会将属性设置为相应的值。

2. 虚拟化对象

Proxy 对象的一个常见应用场景是对象虚拟化,可以使用 Proxy 对象来创建由真实数据支持但具有附加功能或行为的虚拟对象。 以下是一个简单的示例,展示了如何使用 Proxy 对象来实现对象虚拟化:

let data = {
  name: 'John',
  age: 30
};

let handler = {
  get: function(target, prop) {
    if (prop === 'name') {
      return target[prop].toUpperCase();
    } else {
      return target[prop];
    }
  },
  set: function(target, prop, value) {
    if (prop === 'age') {
      if (typeof value !== 'number' || value < 0 || value > 120) {
        throw new Error('Invalid age');
      }
    }

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

let person = new Proxy(data, handler);

console.log(person.name); // Output: JOHN
console.log(person.age); // Output: 30

person.age = 31; // Output: 31
person.age = '31'; // Output: Uncaught Error: Invalid age

当使用person对象读取name属性时,会调用handler对象的get,并将属性值转换为大写字母。当设置age属性时,会调用handler对象的set钩子函数,并验证属性的值是否为数字,并且是否在 0 到 120 的范围内。如果不是,它会抛出一个错误。否则,它会将属性设置为相应的值。

3. 数据绑定

Proxy 对象的一个常见应用场景是数据绑定,可以使用 Proxy 对象来创建多个数据之间的绑定,以确保它们在任何时候都保持同步。 以下是一个简单的示例,展示了如何使用 Proxy 对象来实现数据绑定:

let person1 = {
  name: 'John',
  age: 30
};

let person2 = new Proxy({}, {
  get: function(target, prop, receiver) {
    return person1[prop];
  },
  set: function(target, prop, value, receiver) {
    person1[prop] = value;
    return true;
  }
});

console.log(person1.name); // Output: John
console.log(person2.name); // Output: John

person1.name = 'Jane';

console.log(person1.name); // Output: Jane
console.log(person2.name); // Output: Jane

person2.name = 'Jim';

console.log(person1.name); // Output: Jim
console.log(person2.name); // Output: Jim

当使用person2对象读取属性时,会调用handler对象的get钩子函数,并返回 person1 对应的属性值。当设置 person2 对象的属性时,会调用 handler 对象的 set 钩子函数,并将属性的值设置为 person1 对应的属性值。

参考链接:juejin.cn/post/733135…