How to get proxy's handler from proxy object?

2019-01-25 10:09发布

问题:

For example, if I have this handler/proxy (from the MDN example)...

var handler = {
    get: function(target, name){
        return name in target?
            target[name] :
            37;
    }
};

var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37

is it possible to probe the proxy, p, in some way that allows me to get the handler object back.

Something along the lines of:

p.__handler__   // returns handler object -> Object {get: handler.get(), set: handler.set(), ...}
p.__handler__.get  // returns get prop/fn of handler -> function(target, name){ ...}

Obviously, the various traps set up in the handler are still "known" to the proxy, but is there a clear-cut way to return them/ the handler from the proxy itself? If so, how?

I have no specific use-case for this at the moment, but I could see this being useful if you wanted to dynamically change a handler/traps after you already have a proxy.

回答1:

ECMAScript provides no way to access the internal [[ProxyHandler]] nor [[ProxyTarget]] slots.

Some implementations may provide some non-standard ways, but don't take it for granted.

For example, on Firefox privileged code, you can know if an object is a proxy using

Components.utils.isProxy(object);

I proposed implementing similar methods to expose the [[ProxyHandler]] and [[ProxyTarget]]. They told me to implement them in Debugger.Object instead of Components.utils.

When the patch lands, it will be possible to use something like

Components.utils.import('resource://gre/modules/jsdebugger.jsm');
var Cc = Components.classes;

// Add a debugger to a new global
var global = new Components.utils.Sandbox(
  Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal),
  { freshZone: true }
);
addDebuggerToGlobal(global);
var dbg = new global.Debugger().addDebuggee(this);

// Create a debugger object of your object, and run proxy getters
var dbgObj = dbg.makeDebuggeeValue(object);
if(dbgObj.isProxy) { // a boolean
  dbgObj.proxyHandler.unsafeDereference(); // the [[ProxyHandler]]
  dbgObj.proxyTarget.unsafeDereference(); // the [[ProxyTarget]]
}


回答2:

Add a "special" self descriptor property to getOwnPropertyDescriptor

const target = {
  //Fns ..
  //Props ...
};

const handler = {
  getOwnPropertyDescriptor(target, prop) {
    if(prop == "[[handler]]"){
        return { configurable: true, enumerable: true, value: this };
    }
    return undefined;
  },
  prop1: 'abcd'
  
};

const proxy = new Proxy(target, handler);

console.log(Object.getOwnPropertyDescriptor(proxy, '[[handler]]').value.prop1);



回答3:

I could see this being useful if you wanted to dynamically change a handler/traps after you already have a proxy

If you just want to add handlers over the (proxied) object you already have access to: you could achieve this by creating a new Proxy that handles the specific traps you want to change, eg:

let newProxyWithDifferentGet = new Proxy(originalProxy, {
  get: (target, key){ ... }
}

If you wanted to access the original Proxy's target:

If you are the original Proxy's author, you can just do something like this when you construct it:

let openedProxy = new Proxy(Object.assign(target, {
  _originalHandler: handler,
  _originalTarget: target
}), handler)

If you're not the author, then whether or not that original target should be available to users is the decision of whoever wrote that original Proxy. If you disagree with that author about their encapsulation, that's a social problem, not a technical one, and this is not specific or unique to ES6's Proxies. If you're consuming open-source code, send a PR upstream explaining why you think the original target should be available to users, or just fork their code with your changes and use that, merging their updates to the original repo as you go.