JavaScript元编程“代理” API简介

JAVASCRIPT中的元编程 (METAPROGRAMMING IN JAVASCRIPT)

In the earlier lesson, we learned about some metaprogramming concepts one of which is intercession. Intercession stands for intervening in an operation or intercepting an operation. For example, if you trying to get the value of an object’s property, but that operation was intercepted by some JavaScript program and a false or modified value was returned instead.

前面的课程中 ,我们了解了一些元编程概念,其中之一是代祷 。 代祷代表干预手术或拦截手术。 例如,如果您尝试获取对象属性的值,但是该操作被某些JavaScript程序拦截,则返回了错误或修改后的值。

Intercession is like a man-in-the-middle attack where data is transformed in-flight between the source and the consumer but done deliberately. The program that sits between the source and the consumer is called Proxy.

调解就像是中间人攻击 ,其中数据在源和使用者之间进行动态转换,但是故意进行的。 位于源和使用者之间的程序称为Proxy

You may have heard about this term while connecting to a remote server through a proxy server that sits between you and the remote server. Here the proxy server is able to hide your identity from the remote server as well as transform data received from the remote server if necessary.

通过位于您和远程服务器之间的代理服务器连接到远程服务器时,您可能听说过此术语。 在这里,代理服务器能够从远程服务器隐藏您的身份,并在必要时转换从远程服务器接收到的数据。

JavaScript gives us the Proxy class in ES2015+ that helps us create a proxy around an object. The Proxy global object is a constructor function (class) and its constructor signature is as follows.

JavaScript在ES2015 +中为我们提供了Proxy类,可帮助我们围绕对象创建代理。 Proxy全局对象是构造函数( class ),其构造函数签名如下。

var proxy = new Proxy(target, handler);

Here, the target is the object for which a proxy needs to be created. The proxy is the object that will be used to perform some operations on the target. For example, proxy.prop = 1 operation would be handled by the handler and its job is to set the value of target.prop to 1.

在此, target是需要为其创建代理的对象。 proxy是将用于对target执行某些操作的target 。 例如, proxy.prop = 1操作将由被处理handler和它的工作是的值设置target.prop1

💡 Both target and handler objects are required and must be descendents of Object and not a primitive value (including null and undefined). Else, TypeError: Cannot create proxy with a non-object as target or handler error is thrown.

target targethandler对象都是必需的,并且必须是Object后代,而不是原始值(包括nullundefined )。 否则, TypeError: Cannot create proxy with a non-object as target or handler错误引发,因此TypeError: Cannot create proxy with a non-object as target or handler

Creating a proxy around target doesn’t prevent one from accessing the target but it should be kept private. The handler object contains specific methods one of which will be invoked by JavaScript when a certain operation is performed on the proxy and that method would be responsible to communicate with the target.

target周围创建代理不会阻止访问target但应将其保持私有状态。 handler对象包含特定的方法,当对proxy执行特定操作时,其中的一种方法将由JavaScript调用,并且该方法将负责与target进行通信。

In the Reflect lesson, we learned about the internal slots and internal methods. Every proxy object has two internal slots viz. [[ProxyTarget]] and [[ProxyHandler]]. When we create a proxy object with new Proxy(), these slots are filled with target and handler arguments respectively.

在“ 反思”课程中,我们了解了内部插槽内部方法 。 每个proxy对象都有两个内部插槽,即。 [[ProxyTarget]][[ProxyHandler]] 。 当我们使用new Proxy()创建proxy对象时,这些插槽分别填充了target参数和handler参数。

Image for post
ECMAScript 2015 Table 5ECMAScript 2015表5

In the Reflect lesson, we also learned about the essential internal methods of all objects and saw a table full of internal methods (table above). These internal methods are trigged by Reflect’s static methods. Guess what, a proxy object also has these exact same internal methods as shown in the table below. These method signatures are as same as the above table.

在“ 反射”课程中,我们还了解了所有对象的基本内部方法 ,并看到了一个充满内部方法的 (上 )。 这些内部方法由Reflect的静态方法触发。 猜猜什么, proxy对象也具有这些完全相同的内部方法,如下表所示。 这些方法签名与上表相同。

Image for post
ECMAScript 2015 Table 30ECMAScript 2015表30

When the proxy’s internal method is called, it calls the target’s internal method with the exact same name. For example, if proxy[[Get]]() is called when proxy.prop is accessed (prop is just a property name), it will trigger target[[Get]]() method. This is called no-op forwarding since proxt’s is not performing any operation of its own.

调用proxy的内部方法时,它将使用完全相同的名称调用target的内部方法。 例如,如果在访问proxy.prop时调用proxy[[Get]]() ( prop 只是一个属性名 ),它将触发target[[Get]]()方法。 这称为no-op转发,因为proxt自身不执行任何操作。

However, we can provide an implementation of the internal method of the proxy using the handler. In the right-side column of the above table, we have the method (property) names that will be used to trap the internal method call of the proxy object. For example, handler.get method would trap the [[Get]] internal method call.

但是,我们可以使用handler proxy内部方法的实现。 在上表的右侧列中,我们具有方法( 属性 )名称,这些名称将用于捕获 proxy对象的内部方法调用。 例如, handler.get方法将捕获[[Get]]内部方法调用。

These handler methods are called traps as they trap or intercept the native operation of the proxy, therefore the native operation of the target. This method receives target as the first argument and the rest of the arguments are received as per the internal method specification.

这些处理程序方法称为陷阱,因为它们捕获或拦截proxy的本机操作,因此也就是target的本机操作。 此方法接收target作为第一个参数,其余参数根据内部方法规范接收。

If you read the Reflect lesson, then you know that the Reflect’s static method calls the internal methods of the target. So the Reflect.get(target, prop, receiver) calls the target[[Get]](prop, receiver) internal method which returns the value of the prop property of the target object.

如果您阅读了Reflect课,那么您就会知道Reflect的静态方法将调用target的内部方法。 因此, Reflect.get( target , prop , receiver )调用target[[Get]]( prop , receiver )内部方法,该方法返回target对象的prop属性值。

A trap receives arguments as you would call a Reflect method with the same name. So in this case, handler.get trap would receive target, prop, and receiver as the argument. Now it is up to you to communicate with the target and return the result back.

陷阱会接收参数,就像您将调用具有相同名称的Reflect方法一样。 所以在这种情况下, handler.get陷阱将收到targetprop ,以及receiver作为参数。 现在,您可以与target进行通信并将结果返回。

This also means that you can call an equivalent Reflect method from within a trap and pass all the arguments to the Reflect method without even looking. This is the reason why Reflect static method and Proxy’s handler methods (traps) share the same method signature.

这也意味着您可以从陷阱中调用等效的Reflect方法,并将所有参数传递给Reflect方法,而无需查看。 这就是Reflect静态方法和Proxy的处理程序方法( 陷阱 )共享相同方法签名的原因。

So if a trap is missing, then internal methods of the proxy object gets called which calls the internal method of the target and the proxy would behave as in there is no proxy at all. Meaning proxy.prop is equivalent to calling Reflect.get(target, prop) which is somewhat equivalent to target.prop.

因此,如果缺少陷阱,则会调用proxy对象的内部方法,该方法将调用target的内部方法,并且代理的行为将与根本没有代理一样。 含义proxy.prop等效于调用Reflect.get( target , prop ) ,后者在某种程度上等效于target.prop

If a trap is provided on the handler, you intercept the operation on the target such as if handler.get is present, so proxy.prop would execute the handler.get method and you can communicate with the target using a native approach or use the Reflect.get.

如果被设置在一个陷阱handler ,则拦截对操作target诸如如果handler.get存在,所以proxy.prop将执行handler.get方法和可以与所述通信target使用本机方法或使用Reflect.get

Therefore, for every Reflect static method name, there is an equivalent trap for the handler. You can just go through the Reflect lesson and understand the method signature of the Reflect’s static method because these would be method signature of the traps available in the handler. So we are not gonna go through them in this lesson and jump straight to examples.

因此,对于每个Reflect静态方法名称,该handler都有一个等效的陷阱。 您只需要阅读Reflect的课程,并了解Reflect的静态方法的方法签名,因为这些将是handler可用陷阱的方法签名。 因此,我们在本课程中不会通过它们来直接学习示例。

💡 You can find the list of all supported methods on this MDN documentation but it would be the same as Reflect’s static method signatures.

M您可以在 MDN文档中找到所有受支持方法的列表,但它与Reflect的静态方法签名相同

getset陷阱 (The get and set traps)

Let’s create a proxy for a simple object that contains name and age properties. The name property contains an object with fname and lname properties to represent a person’s name. What we want to achieve make name property look like it is a string. Therefore when a user accesses it, it will come as a string and when the user sets a string value, the string value will be split between the fname and lname properties.

让我们为包含nameage属性的简单对象创建代理。 name属性包含一个对象,该对象具有fnamelname属性来表示一个人的名字。 我们要实现的目标是使name属性看起来像一个字符串。 因此,当用户访问它时,它将作为string并且当用户设置string值时,该string值将在fnamelname属性之间分割。

Image for post
proxy/get-set-traps.jsproxy / get-set-traps.js

In the above example, we have created a proxy for the target object with get and set traps. With this, whenever proxy encounters get operations using proxy.prop syntax or Reflect.get(proxy, ...), the get trap would be executed. Same goes for the set operation.

在上面的示例中,我们使用getset陷阱为target对象创建了proxy 。 有了这个,只要proxy的遭遇get使用操作proxy.prop语法或Reflect.get( proxy , ...)get陷阱将被执行。 set操作也是如此。

In the get trap, we have accessed the property value on the target returned a transformed response (when prop is name). In the case of set trap, we are updating property value in the target and returning a boolean because that’s the signature of the corresponding Reflect.set method call.

get陷阱中,我们已经访问了target上的属性值,并返回了转换后的响应( prop name )。 在set陷阱的情况下,我们将更新target属性值并返回一个boolean因为这是相应Reflect. set的签名Reflect. set Reflect. set方法调用。

💡 You can also use {} as the handler value. In this case, none of the target operations will be intercepted by the proxy handler and it will be directly processed by internal methods of the proxy.

also您也可以使用{}作为handler值。 在这种情况下,没有任何的target运行将由代理截获handler ,它会通过内部方法直接处理proxy

Image for post
proxy/get-set-traps.jsproxy / get-set-traps.js

From the logs, you can see that whenever we assigned a value to proxy, the respective set trap was called. Since there is no has trap on the handler, the "age" in proxy operation (or Reflect.has(proxy, "age")) would be carried out on the target without any intercession.

从日志中可以看到,每当我们为proxy分配一个值时,就会调用相应的set陷阱。 由于没有has陷阱handler中, "age" in proxy操作( Reflect. has (proxy, "age")将是对执行target没有任何说情。

preventExtensions陷阱 (The preventExtensions trap)

Using a proxy, you can prevent the target object getting injected with arbitrary properties. For this, we need to use the preventExtensions trap.

使用代理,可以防止target对象被注入任意属性。 为此,我们需要使用preventExtensions陷阱。

Image for post
proxy/preventExtensions.jsproxy / preventExtensions.js

In the above example, preventExtensions trap is called when Object.preventExtensions or Reflect.preventExtensions method is called on the proxy. Through this trap, we are freezing the target using Object.freeze() method (Object.seal) which will prevent the addition of any new properties to the target.

在上面的示例中,当Object.preventExtensionsReflect. preventExtensions时,将调用preventExtensions陷阱Reflect. preventExtensionsproxy上调用了Reflect. preventExtensions方法。 通过这个陷阱,我们使用Object. freeze ()冻结target Object. freeze () Object. freeze ()方法( Object. seal ),这将防止添加任何新的属性到目标的。

Though this works, it would change the behavior of the target as you can see in the above example, the target.salary operation was unsuccessful as the target was frozen by the proxy in an earlier call. I would recommend adding some logic inside the set trap to prevent any new property addition when proxy is non-extensible instead of mutating the target object.

尽管此方法有效,但可以更改target的行为,如您在上例中target.salarytarget.salary操作未成功,因为target在较早的调用中已被代理冻结。 我建议在set陷阱内添加一些逻辑,以防止proxy不可扩展时添加任何新属性,而不是使target对象发生变异。

construct陷阱 (The construct trap)

You can use proxy as a means to enforce a singleton pattern.

您可以使用代理来强制执行单例模式。

Image for post
proxy/construct.jsproxy / construct.js

In the above example, we have created the PersonProxy for the Person class (constructor function) and we are intercepting the instantiation operation using the construct trap. Hence whenever new PersonProxy() call is made or Reflect.construct(PersonProxy, ...) call is made, construct trap is executed which would pass all the control to Reflect.construct.

在上面的示例中,我们为Person类创建了PersonProxy ( 构造函数 ),并使用construct陷阱拦截了实例化操作。 因此,无论何时进行new PersonProxy()调用或Reflect. construct ( PersonProxy , ...) Reflect. construct ( PersonProxy , ...)调用,执行construct陷阱,该陷阱会将所有控件传递给Reflect.construct

可撤销代理 (Revocable proxy)

A revocable proxy is an object containing proxy field which contains the actual proxy and revoke field which is a function which when called sets the the [[ProxyTarget]] and [[ProxyHandler]] internal slots of the proxy to null. Therefore, any further operations on the proxy would result in a TypeError.

可撤销代理是一个包含proxy字段的对象,该对象包含实际的代理和revoke字段,该字段是一个函数,当调用该函数时, [[ProxyHandler]] proxy[[ProxyTarget]][[ProxyHandler]]内部插槽设置为null 。 因此,对proxy任何进一步的操作都将导致TypeError

var { proxy, revoke } = Proxy.revocable(target, handler);

The Proxy.revocable method returns a revocable proxy object. A revocable proxy would be ideal when a third-party API needs the access of target but you want to intercept operations performed by this third-party API on the target. In that case, you can only provide the proxy object.

Proxy. revocable Proxy. revocable方法返回可撤销代理对象。 当第三方API需要访问target但您想拦截此第三方API在target上执行的操作时,可撤销代理将是理想的选择。 在这种情况下,您只能提供proxy对象。

This abstracts the target object from the third-part API. Once you do not want this third-part API to make any more changes to the target, you can just call the revoke method. Any further operations on the proxy would then result in a TypeError error.

这将从第三方API中提取target对象。 一旦您不希望此第三方API对target进行更多更改,就可以调用revoke方法。 proxy上的任何进一步操作都将导致TypeError错误。

Image for post
proxy/revocable-proxy.jsproxy / revocable-proxy.js

As you can see from the above example, once revoke has been called, any further operations on the proxy would result in a TypeError exception, whether or not that operation has a trap in the handler.

从上面的示例可以看到,一旦调用了revoke ,对proxy任何进一步操作都将导致TypeError异常,无论该操作是否在handler有陷阱。

Image for post
thatisuday.comthatisuday.com GitHubGitHub TwitterTwitter StackOverflowStackOverflow / / InstagramInstagram

翻译自: https://medium.com/jspoint/introduction-to-proxy-api-for-metaprogramming-in-javascript-fa2781e360ba

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值