proxy-pattern

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Proxy Pattern

Proxy 模式

With a Proxy object, we get more control over the interactions with certain objects. A proxy object can determine the behavior whenever we're interacting with the object, for example when we're getting a value, or setting a value.
借助Proxy对象,我们可以更精准地控制与特定对象的交互。当我们与对象进行交互时(比如获取值或设置值),代理对象可以决定具体的行为。

When to Use

适用场景

  • Use this when you need to add validation, formatting, notifications, or debugging to object access
  • This is helpful for controlling and intercepting property gets and sets on objects
  • 当你需要为对象访问添加验证、格式化、通知或调试功能时,可以使用该模式
  • 它有助于控制和拦截对象属性的获取与设置操作

Instructions

使用说明

  • Create a
    Proxy
    with a handler object defining
    get
    and
    set
    traps
  • Use the
    Reflect
    object within handlers for cleaner property access and modification
  • Add validation logic in the
    set
    trap to ensure data integrity
  • Avoid using proxies in performance-critical code paths as they add overhead
  • 创建一个带有处理程序对象的
    Proxy
    ,该对象定义
    get
    set
    捕获器
  • 在处理程序中使用
    Reflect
    对象,以更简洁地进行属性访问和修改
  • set
    捕获器中添加验证逻辑,确保数据完整性
  • 避免在性能敏感的代码路径中使用代理,因为它们会带来额外开销

Details

详细说明

Generally speaking, a proxy means a stand-in for someone else. Instead of speaking to that person directly, you'll speak to the proxy person who will represent the person you were trying to reach. The same happens in JavaScript: instead of interacting with the target object directly, we'll interact with the Proxy object.
Let's create a
person
object, that represents John Doe.
js
const person = {
  name: "John Doe",
  age: 42,
  nationality: "American",
};
Instead of interacting with this object directly, we want to interact with a proxy object. In JavaScript, we can easily create a new proxy by creating a new instance of
Proxy
.
js
const person = {
  name: "John Doe",
  age: 42,
  nationality: "American",
};

const personProxy = new Proxy(person, {});
The second argument of
Proxy
is an object that represents the handler. In the handler object, we can define specific behavior based on the type of interaction. Although there are many methods that you can add to the Proxy handler, the two most common ones are
get
and
set
:
  • get
    : Gets invoked when trying to access a property
  • set
    : Gets invoked when trying to modify a property
Instead of interacting with the
person
object directly, we'll be interacting with the
personProxy
.
Let's add handlers to the
personProxy
Proxy. When trying to modify a property, thus invoking the
set
method on the
Proxy
, we want the proxy to log the previous value and the new value of the property. When trying to access a property, thus invoking the
get
method on the
Proxy
, we want the proxy to log a more readable sentence that contains the key and value of the property.
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${obj[prop]}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    obj[prop] = value;
  },
});
When accessing the
name
property, the Proxy returned a better sounding sentence:
The value of name is John Doe
.
When modifying the
age
property, the Proxy returned the previous and new value of this property:
Changed age from 42 to 43
.
A proxy can be useful to add validation. A user shouldn't be able to change
person
's age to a string value, or give them an empty name. Or if the user is trying to access a property on the object that doesn't exist, we should let the user know.
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    if (!obj[prop]) {
      console.log(
        `Hmm.. this property doesn't seem to exist on the target object`
      );
    } else {
      console.log(`The value of ${prop} is ${obj[prop]}`);
    }
  },
  set: (obj, prop, value) => {
    if (prop === "age" && typeof value !== "number") {
      console.log(`Sorry, you can only pass numeric values for age.`);
    } else if (prop === "name" && value.length < 2) {
      console.log(`You need to provide a valid name.`);
    } else {
      console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
      obj[prop] = value;
    }
  },
});
The proxy makes sure that we aren't modifying the
person
object with faulty values, which helps us keep our data pure!
一般来说,代理指的是他人的替身。你不会直接与本人沟通,而是与代表对方的代理进行交流。JavaScript中的代理也是如此:我们不会直接与目标对象交互,而是与Proxy对象交互。
让我们创建一个代表John Doe的
person
对象。
js
const person = {
  name: "John Doe",
  age: 42,
  nationality: "American",
};
我们不希望直接与这个对象交互,而是通过代理对象进行操作。在JavaScript中,我们可以通过创建
Proxy
的新实例轻松生成一个新代理。
js
const person = {
  name: "John Doe",
  age: 42,
  nationality: "American",
};

const personProxy = new Proxy(person, {});
Proxy
的第二个参数是代表处理程序(handler)的对象。在处理程序对象中,我们可以根据交互类型定义特定行为。虽然你可以为Proxy处理程序添加许多方法,但最常用的两个是
get
set
  • get
    :当尝试访问属性时触发
  • set
    :当尝试修改属性时触发
我们将不再直接与
person
对象交互,而是与
personProxy
交互。
让我们为
personProxy
代理添加处理程序。当尝试修改属性(即触发Proxy的
set
方法)时,我们希望代理记录该属性的旧值和新值。当尝试访问属性(即触发Proxy的
get
方法)时,我们希望代理输出一句更易读的语句,包含属性的键和值。
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${obj[prop]}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    obj[prop] = value;
  },
});
当访问
name
属性时,代理会返回一句更友好的语句:
The value of name is John Doe
当修改
age
属性时,代理会返回该属性的旧值和新值:
Changed age from 42 to 43
代理可用于添加验证功能。用户不能将
person
的年龄修改为字符串值,也不能设置空名称。如果用户尝试访问对象上不存在的属性,我们应该提示用户。
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    if (!obj[prop]) {
      console.log(
        `Hmm.. this property doesn't seem to exist on the target object`
      );
    } else {
      console.log(`The value of ${prop} is ${obj[prop]}`);
    }
  },
  set: (obj, prop, value) => {
    if (prop === "age" && typeof value !== "number") {
      console.log(`Sorry, you can only pass numeric values for age.`);
    } else if (prop === "name" && value.length < 2) {
      console.log(`You need to provide a valid name.`);
    } else {
      console.log(`Changed ${prop} from ${obj[prop]} to ${value}.`);
      obj[prop] = value;
    }
  },
});
代理确保我们不会用错误的值修改
person
对象,这有助于保持数据的纯净性!

Reflect

Reflect
对象

JavaScript provides a built-in object called
Reflect
, which makes it easier for us to manipulate the target object when working with proxies.
Previously, we tried to modify and access properties on the target object within the proxy through directly getting or setting the values with bracket notation. Instead, we can use the
Reflect
object. The methods on the
Reflect
object have the same name as the methods on the
handler
object.
Instead of accessing properties through
obj[prop]
or setting properties through
obj[prop] = value
, we can access or modify properties on the target object through
Reflect.get()
and
Reflect.set()
. The methods receive the same arguments as the methods on the handler object.
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    Reflect.set(obj, prop, value);
  },
});
We can access and modify the properties on the target object easily with the
Reflect
object.
JavaScript提供了一个名为
Reflect
的内置对象,它让我们在使用代理时更轻松地操作目标对象。
之前,我们在代理中通过方括号表示法直接获取或设置值来操作目标对象的属性。现在,我们可以使用
Reflect
对象。
Reflect
对象上的方法与处理程序对象上的方法名称相同。
我们不再通过
obj[prop]
访问属性或
obj[prop] = value
设置属性,而是可以通过
Reflect.get()
Reflect.set()
来访问或修改目标对象的属性。这些方法接收的参数与处理程序对象上的方法相同。
js
const personProxy = new Proxy(person, {
  get: (obj, prop) => {
    console.log(`The value of ${prop} is ${Reflect.get(obj, prop)}`);
  },
  set: (obj, prop, value) => {
    console.log(`Changed ${prop} from ${obj[prop]} to ${value}`);
    Reflect.set(obj, prop, value);
  },
});
借助
Reflect
对象,我们可以轻松地访问和修改目标对象的属性。

Tradeoffs

权衡利弊

Proxies are a powerful way to add control over the behavior of an object. A proxy can have various use-cases: it can help with validation, formatting, notifications, or debugging.
Overusing the
Proxy
object or performing heavy operations on each
handler
method invocation can easily affect the performance of your application negatively. It's best to not use proxies for performance-critical code.
代理是一种强大的方式,可用于控制对象的行为。代理有多种应用场景:它可以帮助实现验证、格式化、通知或调试功能。
过度使用
Proxy
对象或在每次调用处理程序方法时执行繁重操作,很容易对应用程序性能产生负面影响。最好不要在性能敏感的代码中使用代理。

Source

来源

References

参考资料