简体   繁体   中英

javascript Proxy of function apply trap gives no access to the receiver Proxy

When I have a Proxy the get and set traps both provide access to the underlying target Object as well as the Proxy receiver of the initial access request. This is good, because for some "pseudo properties" of the Proxy that I need, I look up the Proxy on a WeakMap to find an extension object which has such properties that apply to the Proxy but not to the underlying target object.

When the underlying target Object is a Function, then the apply trap will work, but its arguments are only the naked function, and the thisArg and the arguments, not the Proxy receiver of the initial apply. So now my ability to look up extension properties to the Proxy fail.

Is there any other way that I can get back to the Proxy from within the apply trap? Shouldn't that be considered a bug in the Proxy specification? Or at least an inconsistency? If the Proxy is considered useful in get and set traps, why is it not considered useful in the apply trap?

I'd like to first give an answer to the implicit question "Why do some traps have a receiver and others don't?". See here for a list of all traps. I was at first missing myself a reference to the receiver within some of these functions. But the logic is as follows: All these traps are interceptors for functions that exist independently from the Proxy : the object internal methods which are invoked whenever an interaction with an object takes place. These internal methods have been made accessible via the static functions of the namespace Reflect . These static functions have the same arguments as those of the proxy handler, yet they are meaningful without a proxy (even though apparently the Reflect API was designed having proxies in mind, ie making the internal methods available to the proxy). Each of these functions has as many arguments as it needs to perform what it does (without any proxy around). Adding an additional receiver argument to Reflect.apply would be meaningless without a proxy around as one already passes a thisArg for the function application. On the other hand, Reflect.get gets the getter for a property of an object and then applies it - usually with the object itself as this , but one might want to replace this by any other object (in particular in the case of a prototype chain, where the getter of a prototype is applied to a descendant). Therefore the extra argument receiver makes sense even without a proxy around.

And as @Bergi pointed out in the comments, even where present as argument in a trap function, the receiver isn't necessarily identical to the proxy anyway, but is simply the this of a getter or setter in this context, so can be a prototype descendant of a proxy or even something completely different. To understand the latter, consider the following: If one calls the Reflect functions with a proxy as the target argument, this leads to a call of the corresponding trap function with the proxy replaced by the target of the proxy. Eg the call Reflect.get(proxy, 'value', exoticObject) will lead to a call of handler.get with proxy replaced by the target of the proxy (let's call it proxyTarget ) but the other arguments unchanged, ie: handler.get(proxyTarget, 'value', exoticObject) . So in this example, the receiver is not even related to the proxy.

Now for the question how to access the proxy from within the traps: One possibility is to create a specific handler for each proxy and store a reference to the proxy as property of the handler. The handler is always accessible within the traps via "this". So this.proxy would give you the proxy within each of the trap functions.

The OPs question mentions also an extension object with supplemental properties and a WeakMap to obtain it. Instead of using the proxy to look up the extension in the WeakMap one could alternatively as well save a reference to the extension in the handler and access it via this.extension in the traps.

I want to provide code that takes the suggestion by @Sebastian https://stackoverflow.com/a/74467044/7666635 and shows how I can always have access to the proxy from a closure:

function makeProxyFor(object) {
    const proxy = new Proxy(object, {
        get: function(target, prop, receiver) {
            console.log("get", proxy, target, prop, receiver);
            const result = Reflect.get(target, prop, receiver);
            if(typeof result == 'function' || typeof result == 'object')
                return makeProxyFor(result);
            else
                return result;
        },
        apply: function proxyGet(func, thisArg, args) {
            console.log("apply", proxy, func, thisArg, args);
            const result = func.apply(thisArg, args);
            if(typeof result == 'function' || typeof result == 'object')
                return makeProxyFor(result);
            else
                return result;
        }
    });
    return proxy;
}

Example dialog:

> p = makeProxyFor({ x: function() { return { x: 'x'}; } })
< Proxy {x: ƒ}
> p.x().x
+ get Proxy {x: ƒ} {x: ƒ} x Proxy {x: ƒ}
+ apply Proxy {length: 0, name: 'x', arguments: null, caller: null, prototype: {…}} ƒ () { return { x: 'x'}; } Proxy {x: ƒ} []
+ get Proxy {x: 'x'} {x: 'x'} x Proxy {x: 'x'}
< 'x'

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM