The following table shows Javascript code on the left, and approximately how that code is trapped and interpreted by the Proxy mechanism on the right.
Assume proxy
is defined as:
var proxy = new Proxy(target, handler)
See further notes below for details.
Syntactic operations that can be intercepted | ||
Operation | Code | Trapped as |
---|---|---|
property access | proxy.foo proxy['foo'] |
handler.get(target, 'foo', proxy) |
property assignment | proxy.foo = v proxy['foo'] = v |
handler.set(target, 'foo', v, proxy) |
property invocation (1) | proxy.foo(1,2,3) | handler.get(target, 'foo', proxy).apply(proxy, [1,2,3]) |
property query | 'foo' in proxy | handler.has(target, 'foo') |
property deletion | delete proxy.foo delete proxy['foo'] |
handler.deleteProperty(target, 'foo') |
property enumeration | for (var prop in proxy) { ... } | var $iterator = handler.enumerate(target); var $nxt = iterator.next(); while (!$nxt.done) { var prop = String($nxt.value); ... $nxt = $iterator.next(); } |
inherited property access | var obj = Object.create(proxy); obj.foo; |
handler.get(target, 'foo', obj) |
inherited property assignment | var obj = Object.create(proxy); obj.foo = v; |
handler.set(target, 'foo', v, obj) |
Operations on function proxies | ||
function call (3) | proxy(...args) | handler.apply(target, undefined, args) |
function construct | new proxy(...args) | handler.construct(target, args, proxy) |
method call (4) | object.proxy(...args) | handler.apply(target, object, args) |
Function.prototype.call (5) | proxy.call(object, ...args) | handler.apply(target, object, args) |
Non-interceptable operators | ||
typeof test | typeof proxy | (typeof target === "function") ? "function" : "object" |
identity comparison | proxy === v | proxy === v |
Built-ins inherited from Object.prototype | ||
hasOwnProperty (6) | proxy.hasOwnProperty('foo') | handler.hasOwn(target, 'foo') |
valueOf (7) | proxy.valueOf() | target.valueOf() |
toString (8) | proxy.toString() | target.toString() |
(Static) operations on Object | ||
getOwnPropertyNames | Object.getOwnPropertyNames(proxy) | handler.ownKeys(target) |
getOwnPropertyDescriptor (9) | Object.getOwnPropertyDescriptor(proxy, 'foo') | handler.getOwnPropertyDescriptor(target, 'foo') |
defineProperty (10) | Object.defineProperty(proxy, 'foo', {value:42}) | handler.defineProperty(target, 'foo', {value:42,writable:true,enumerable:true,configurable:true}) |
defineProperties (10) | Object.defineProperties(proxy, {foo: {value:42}}) | handler.defineProperty(target, 'foo', {value:42,writable:true,enumerable:true,configurable:true}) |
preventExtensions | Object.preventExtensions(proxy) | handler.preventExtensions(target) |
isExtensible | Object.isExtensible(proxy) | handler.isExtensible(target) |
getPrototypeOf | Object.getPrototypeOf(proxy) | handler.getPrototypeOf(target) |
setPrototypeOf (11) | Object.setPrototypeOf(proxy, newProto) | handler.setPrototypeOf(target, newProto) |
keys (12) | Object.keys(proxy) | handler.ownKeys(target) |
- (1): in Javascript, a method call like
obj.foo(1,2,3)
is defined as looking up the "foo" property onobj
, and then calling the resulting function withobj
as thethis
-binding. Ifobj
is a proxy, the same strategy applies. There is no separateinvoke
trap. - (3): the syntax
...args
is ECMAScript 6 syntax for "spreading" arguments into a call.f(...[1,2,3])
is equivalent tof(1,2,3)
. Function calls can only be intercepted if the target is a function, i.e.typeof target === "function"
. - (4): this assumes that the proxy was installed as a method on
object
, e.g.var object = { proxy: new Proxy(target, handler) }
. - (5): assuming that
proxy.call
, which triggers the proxy's "get" trap, returnedFunction.prototype.call
. - (6): assuming that
proxy.hasOwnProperty
, which triggers the proxy's "get" trap, returnedObject.prototype.hasOwnProperty
. - (7): assuming that
proxy.valueOf
, which triggers the proxy's "get" trap, returnedObject.prototype.valueOf
. - (8): assuming that
proxy.toString
, which triggers the proxy's "get" trap, returnedObject.prototype.toString
. - (9): the return value of the
getOwnPropertyDescriptor
trap (the property descriptor object) is not the original value returned from the interceptedObject.getOwnPropertyDescriptor
call. Rather, it is a fresh descriptor object that is guaranteed to be "complete" (i.e. to define values for all relevant ECMAScript property attributes). - (10): the third argument to the
defineProperty
trap (the property descriptor object) is not the original value passed into the interceptedObject.defineProperty
call. Rather, it is a fresh descriptor object that is guaranteed to be "complete" (i.e. to define values for all relevant ECMAScript property attributes). ForObject.defineProperties(proxy,props)
, the proxy'sdefineProperty
trap is called for each own enumerable property ofprops
. - (11): only on platforms that support mutable
__proto__
and where__proto__
is an accessor property defined onObject.prototype
. - (12): the
ownKeys
trap must return all own property names.Object.keys
then retains only the keys denoting enumerable properties.