10.5 Proxy Object Internal Methods and Internal Slots

A proxy object is an exotic object whose essential internal methods are partially implemented using ECMAScript code. Every proxy object has an internal slot called [[ProxyHandler]]. The value of [[ProxyHandler]] is an object, called the proxy's handler object, or null. Methods (see Table 32) of a handler object may be used to augment the implementation for one or more of the proxy object's internal methods. Every proxy object also has an internal slot called [[ProxyTarget]] whose value is either an object or the null value. This object is called the proxy's target object.

An object is a Proxy exotic object if its essential internal methods (including [[Call]] and [[Construct]], if applicable) use the definitions in this section. These internal methods are installed in ProxyCreate.

Table 32: Proxy Handler Methods
Internal Method Handler Method
[[GetPrototypeOf]] getPrototypeOf
[[SetPrototypeOf]] setPrototypeOf
[[IsExtensible]] isExtensible
[[PreventExtensions]] preventExtensions
[[GetOwnProperty]] getOwnPropertyDescriptor
[[DefineOwnProperty]] defineProperty
[[HasProperty]] has
[[Get]] get
[[Set]] set
[[Delete]] deleteProperty
[[OwnPropertyKeys]] ownKeys
[[Call]] apply
[[Construct]] construct

When a handler method is called to provide the implementation of a proxy object internal method, the handler method is passed the proxy's target object as a parameter. A proxy's handler object does not necessarily have a method corresponding to every essential internal method. Invoking an internal method on the proxy results in the invocation of the corresponding internal method on the proxy's target object if the handler object does not have a method corresponding to the internal trap.

The [[ProxyHandler]] and [[ProxyTarget]] internal slots of a proxy object are always initialized when the object is created and typically may not be modified. Some proxy objects are created in a manner that permits them to be subsequently revoked. When a proxy is revoked, its [[ProxyHandler]] and [[ProxyTarget]] internal slots are set to null causing subsequent invocations of internal methods on that proxy object to throw a TypeError exception.

Because proxy objects permit the implementation of internal methods to be provided by arbitrary ECMAScript code, it is possible to define a proxy object whose handler methods violates the invariants defined in 6.1.7.3. Some of the internal method invariants defined in 6.1.7.3 are essential integrity invariants. These invariants are explicitly enforced by the proxy object internal methods specified in this section. An ECMAScript implementation must be robust in the presence of all possible invariant violations.

In the following algorithm descriptions, assume O is an ECMAScript proxy object, P is a property key value, V is any ECMAScript language value and Desc is a Property Descriptor record.

10.5.1 [[GetPrototypeOf]] ( )

The [[GetPrototypeOf]] internal method of a Proxy exotic object O takes no arguments. It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Let trap be ? GetMethod(handler, "getPrototypeOf").
  6. If trap is undefined, then
    1. Return ? target.[[GetPrototypeOf]]().
  7. Let handlerProto be ? Call(trap, handler, « target »).
  8. If Type(handlerProto) is neither Object nor Null, throw a TypeError exception.
  9. Let extensibleTarget be ? IsExtensible(target).
  10. If extensibleTarget is true, return handlerProto.
  11. Let targetProto be ? target.[[GetPrototypeOf]]().
  12. If SameValue(handlerProto, targetProto) is false, throw a TypeError exception.
  13. Return handlerProto.
Note

[[GetPrototypeOf]] for proxy objects enforces the following invariants:

  • The result of [[GetPrototypeOf]] must be either an Object or null.
  • If the target object is not extensible, [[GetPrototypeOf]] applied to the proxy object must return the same value as [[GetPrototypeOf]] applied to the proxy object's target object.

10.5.2 [[SetPrototypeOf]] ( V )

The [[SetPrototypeOf]] internal method of a Proxy exotic object O takes argument V (an Object or null). It performs the following steps when called:

  1. Assert: Either Type(V) is Object or Type(V) is Null.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "setPrototypeOf").
  7. If trap is undefined, then
    1. Return ? target.[[SetPrototypeOf]](V).
  8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, V »)).
  9. If booleanTrapResult is false, return false.
  10. Let extensibleTarget be ? IsExtensible(target).
  11. If extensibleTarget is true, return true.
  12. Let targetProto be ? target.[[GetPrototypeOf]]().
  13. If SameValue(V, targetProto) is false, throw a TypeError exception.
  14. Return true.
Note

[[SetPrototypeOf]] for proxy objects enforces the following invariants:

  • The result of [[SetPrototypeOf]] is a Boolean value.
  • If the target object is not extensible, the argument value must be the same as the result of [[GetPrototypeOf]] applied to target object.

10.5.3 [[IsExtensible]] ( )

The [[IsExtensible]] internal method of a Proxy exotic object O takes no arguments. It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Let trap be ? GetMethod(handler, "isExtensible").
  6. If trap is undefined, then
    1. Return ? IsExtensible(target).
  7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).
  8. Let targetResult be ? IsExtensible(target).
  9. If SameValue(booleanTrapResult, targetResult) is false, throw a TypeError exception.
  10. Return booleanTrapResult.
Note

[[IsExtensible]] for proxy objects enforces the following invariants:

  • The result of [[IsExtensible]] is a Boolean value.
  • [[IsExtensible]] applied to the proxy object must return the same value as [[IsExtensible]] applied to the proxy object's target object with the same argument.

10.5.4 [[PreventExtensions]] ( )

The [[PreventExtensions]] internal method of a Proxy exotic object O takes no arguments. It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Let trap be ? GetMethod(handler, "preventExtensions").
  6. If trap is undefined, then
    1. Return ? target.[[PreventExtensions]]().
  7. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target »)).
  8. If booleanTrapResult is true, then
    1. Let extensibleTarget be ? IsExtensible(target).
    2. If extensibleTarget is true, throw a TypeError exception.
  9. Return booleanTrapResult.
Note

[[PreventExtensions]] for proxy objects enforces the following invariants:

  • The result of [[PreventExtensions]] is a Boolean value.
  • [[PreventExtensions]] applied to the proxy object only returns true if [[IsExtensible]] applied to the proxy object's target object is false.

10.5.5 [[GetOwnProperty]] ( P )

The [[GetOwnProperty]] internal method of a Proxy exotic object O takes argument P (a property key). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "getOwnPropertyDescriptor").
  7. If trap is undefined, then
    1. Return ? target.[[GetOwnProperty]](P).
  8. Let trapResultObj be ? Call(trap, handler, « target, P »).
  9. If Type(trapResultObj) is neither Object nor Undefined, throw a TypeError exception.
  10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  11. If trapResultObj is undefined, then
    1. If targetDesc is undefined, return undefined.
    2. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
    3. Let extensibleTarget be ? IsExtensible(target).
    4. If extensibleTarget is false, throw a TypeError exception.
    5. Return undefined.
  12. Let extensibleTarget be ? IsExtensible(target).
  13. Let resultDesc be ? ToPropertyDescriptor(trapResultObj).
  14. Call CompletePropertyDescriptor(resultDesc).
  15. Let valid be IsCompatiblePropertyDescriptor(extensibleTarget, resultDesc, targetDesc).
  16. If valid is false, throw a TypeError exception.
  17. If resultDesc.[[Configurable]] is false, then
    1. If targetDesc is undefined or targetDesc.[[Configurable]] is true, then
      1. Throw a TypeError exception.
    2. If resultDesc has a [[Writable]] field and resultDesc.[[Writable]] is false, then
      1. If targetDesc.[[Writable]] is true, throw a TypeError exception.
  18. Return resultDesc.
Note

[[GetOwnProperty]] for proxy objects enforces the following invariants:

  • The result of [[GetOwnProperty]] must be either an Object or undefined.
  • A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object.
  • A property cannot be reported as non-existent, if the target object is not extensible, unless it does not exist as an own property of the target object.
  • A property cannot be reported as existent, if the target object is not extensible, unless it exists as an own property of the target object.
  • A property cannot be reported as non-configurable, unless it exists as a non-configurable own property of the target object.
  • A property cannot be reported as both non-configurable and non-writable, unless it exists as a non-configurable, non-writable own property of the target object.

10.5.6 [[DefineOwnProperty]] ( P, Desc )

The [[DefineOwnProperty]] internal method of a Proxy exotic object O takes arguments P (a property key) and Desc (a Property Descriptor). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "defineProperty").
  7. If trap is undefined, then
    1. Return ? target.[[DefineOwnProperty]](P, Desc).
  8. Let descObj be FromPropertyDescriptor(Desc).
  9. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, descObj »)).
  10. If booleanTrapResult is false, return false.
  11. Let targetDesc be ? target.[[GetOwnProperty]](P).
  12. Let extensibleTarget be ? IsExtensible(target).
  13. If Desc has a [[Configurable]] field and if Desc.[[Configurable]] is false, then
    1. Let settingConfigFalse be true.
  14. Else, let settingConfigFalse be false.
  15. If targetDesc is undefined, then
    1. If extensibleTarget is false, throw a TypeError exception.
    2. If settingConfigFalse is true, throw a TypeError exception.
  16. Else,
    1. If IsCompatiblePropertyDescriptor(extensibleTarget, Desc, targetDesc) is false, throw a TypeError exception.
    2. If settingConfigFalse is true and targetDesc.[[Configurable]] is true, throw a TypeError exception.
    3. If IsDataDescriptor(targetDesc) is true, targetDesc.[[Configurable]] is false, and targetDesc.[[Writable]] is true, then
      1. If Desc has a [[Writable]] field and Desc.[[Writable]] is false, throw a TypeError exception.
  17. Return true.
Note

[[DefineOwnProperty]] for proxy objects enforces the following invariants:

  • The result of [[DefineOwnProperty]] is a Boolean value.
  • A property cannot be added, if the target object is not extensible.
  • A property cannot be non-configurable, unless there exists a corresponding non-configurable own property of the target object.
  • A non-configurable property cannot be non-writable, unless there exists a corresponding non-configurable, non-writable own property of the target object.
  • If a property has a corresponding target object property then applying the Property Descriptor of the property to the target object using [[DefineOwnProperty]] will not throw an exception.

10.5.7 [[HasProperty]] ( P )

The [[HasProperty]] internal method of a Proxy exotic object O takes argument P (a property key). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "has").
  7. If trap is undefined, then
    1. Return ? target.[[HasProperty]](P).
  8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
  9. If booleanTrapResult is false, then
    1. Let targetDesc be ? target.[[GetOwnProperty]](P).
    2. If targetDesc is not undefined, then
      1. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
      2. Let extensibleTarget be ? IsExtensible(target).
      3. If extensibleTarget is false, throw a TypeError exception.
  10. Return booleanTrapResult.
Note

[[HasProperty]] for proxy objects enforces the following invariants:

  • The result of [[HasProperty]] is a Boolean value.
  • A property cannot be reported as non-existent, if it exists as a non-configurable own property of the target object.
  • A property cannot be reported as non-existent, if it exists as an own property of the target object and the target object is not extensible.

10.5.8 [[Get]] ( P, Receiver )

The [[Get]] internal method of a Proxy exotic object O takes arguments P (a property key) and Receiver (an ECMAScript language value). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "get").
  7. If trap is undefined, then
    1. Return ? target.[[Get]](P, Receiver).
  8. Let trapResult be ? Call(trap, handler, « target, P, Receiver »).
  9. Let targetDesc be ? target.[[GetOwnProperty]](P).
  10. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
    1. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
      1. If SameValue(trapResult, targetDesc.[[Value]]) is false, throw a TypeError exception.
    2. If IsAccessorDescriptor(targetDesc) is true and targetDesc.[[Get]] is undefined, then
      1. If trapResult is not undefined, throw a TypeError exception.
  11. Return trapResult.
Note

[[Get]] for proxy objects enforces the following invariants:

  • The value reported for a property must be the same as the value of the corresponding target object property if the target object property is a non-writable, non-configurable own data property.
  • The value reported for a property must be undefined if the corresponding target object property is a non-configurable own accessor property that has undefined as its [[Get]] attribute.

10.5.9 [[Set]] ( P, V, Receiver )

The [[Set]] internal method of a Proxy exotic object O takes arguments P (a property key), V (an ECMAScript language value), and Receiver (an ECMAScript language value). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "set").
  7. If trap is undefined, then
    1. Return ? target.[[Set]](P, V, Receiver).
  8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P, V, Receiver »)).
  9. If booleanTrapResult is false, return false.
  10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  11. If targetDesc is not undefined and targetDesc.[[Configurable]] is false, then
    1. If IsDataDescriptor(targetDesc) is true and targetDesc.[[Writable]] is false, then
      1. If SameValue(V, targetDesc.[[Value]]) is false, throw a TypeError exception.
    2. If IsAccessorDescriptor(targetDesc) is true, then
      1. If targetDesc.[[Set]] is undefined, throw a TypeError exception.
  12. Return true.
Note

[[Set]] for proxy objects enforces the following invariants:

  • The result of [[Set]] is a Boolean value.
  • Cannot change the value of a property to be different from the value of the corresponding target object property if the corresponding target object property is a non-writable, non-configurable own data property.
  • Cannot set the value of a property if the corresponding target object property is a non-configurable own accessor property that has undefined as its [[Set]] attribute.

10.5.10 [[Delete]] ( P )

The [[Delete]] internal method of a Proxy exotic object O takes argument P (a property key). It performs the following steps when called:

  1. Assert: IsPropertyKey(P) is true.
  2. Let handler be O.[[ProxyHandler]].
  3. If handler is null, throw a TypeError exception.
  4. Assert: Type(handler) is Object.
  5. Let target be O.[[ProxyTarget]].
  6. Let trap be ? GetMethod(handler, "deleteProperty").
  7. If trap is undefined, then
    1. Return ? target.[[Delete]](P).
  8. Let booleanTrapResult be ! ToBoolean(? Call(trap, handler, « target, P »)).
  9. If booleanTrapResult is false, return false.
  10. Let targetDesc be ? target.[[GetOwnProperty]](P).
  11. If targetDesc is undefined, return true.
  12. If targetDesc.[[Configurable]] is false, throw a TypeError exception.
  13. Let extensibleTarget be ? IsExtensible(target).
  14. If extensibleTarget is false, throw a TypeError exception.
  15. Return true.
Note

[[Delete]] for proxy objects enforces the following invariants:

  • The result of [[Delete]] is a Boolean value.
  • A property cannot be reported as deleted, if it exists as a non-configurable own property of the target object.
  • A property cannot be reported as deleted, if it exists as an own property of the target object and the target object is non-extensible.

10.5.11 [[OwnPropertyKeys]] ( )

The [[OwnPropertyKeys]] internal method of a Proxy exotic object O takes no arguments. It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Let trap be ? GetMethod(handler, "ownKeys").
  6. If trap is undefined, then
    1. Return ? target.[[OwnPropertyKeys]]().
  7. Let trapResultArray be ? Call(trap, handler, « target »).
  8. Let trapResult be ? CreateListFromArrayLike(trapResultArray, « String, Symbol »).
  9. If trapResult contains any duplicate entries, throw a TypeError exception.
  10. Let extensibleTarget be ? IsExtensible(target).
  11. Let targetKeys be ? target.[[OwnPropertyKeys]]().
  12. Assert: targetKeys is a List whose elements are only String and Symbol values.
  13. Assert: targetKeys contains no duplicate entries.
  14. Let targetConfigurableKeys be a new empty List.
  15. Let targetNonconfigurableKeys be a new empty List.
  16. For each element key of targetKeys, do
    1. Let desc be ? target.[[GetOwnProperty]](key).
    2. If desc is not undefined and desc.[[Configurable]] is false, then
      1. Append key as an element of targetNonconfigurableKeys.
    3. Else,
      1. Append key as an element of targetConfigurableKeys.
  17. If extensibleTarget is true and targetNonconfigurableKeys is empty, then
    1. Return trapResult.
  18. Let uncheckedResultKeys be a List whose elements are the elements of trapResult.
  19. For each element key of targetNonconfigurableKeys, do
    1. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
    2. Remove key from uncheckedResultKeys.
  20. If extensibleTarget is true, return trapResult.
  21. For each element key of targetConfigurableKeys, do
    1. If key is not an element of uncheckedResultKeys, throw a TypeError exception.
    2. Remove key from uncheckedResultKeys.
  22. If uncheckedResultKeys is not empty, throw a TypeError exception.
  23. Return trapResult.
Note

[[OwnPropertyKeys]] for proxy objects enforces the following invariants:

  • The result of [[OwnPropertyKeys]] is a List.
  • The returned List contains no duplicate entries.
  • The Type of each result List element is either String or Symbol.
  • The result List must contain the keys of all non-configurable own properties of the target object.
  • If the target object is not extensible, then the result List must contain all the keys of the own properties of the target object and no other values.

10.5.12 [[Call]] ( thisArgument, argumentsList )

The [[Call]] internal method of a Proxy exotic object O takes arguments thisArgument (an ECMAScript language value) and argumentsList (a List of ECMAScript language values). It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Let trap be ? GetMethod(handler, "apply").
  6. If trap is undefined, then
    1. Return ? Call(target, thisArgument, argumentsList).
  7. Let argArray be ! CreateArrayFromList(argumentsList).
  8. Return ? Call(trap, handler, « target, thisArgument, argArray »).
Note

A Proxy exotic object only has a [[Call]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Call]] internal method.

10.5.13 [[Construct]] ( argumentsList, newTarget )

The [[Construct]] internal method of a Proxy exotic object O takes arguments argumentsList (a List of ECMAScript language values) and newTarget (a constructor). It performs the following steps when called:

  1. Let handler be O.[[ProxyHandler]].
  2. If handler is null, throw a TypeError exception.
  3. Assert: Type(handler) is Object.
  4. Let target be O.[[ProxyTarget]].
  5. Assert: IsConstructor(target) is true.
  6. Let trap be ? GetMethod(handler, "construct").
  7. If trap is undefined, then
    1. Return ? Construct(target, argumentsList, newTarget).
  8. Let argArray be ! CreateArrayFromList(argumentsList).
  9. Let newObj be ? Call(trap, handler, « target, argArray, newTarget »).
  10. If Type(newObj) is not Object, throw a TypeError exception.
  11. Return newObj.
Note 1

A Proxy exotic object only has a [[Construct]] internal method if the initial value of its [[ProxyTarget]] internal slot is an object that has a [[Construct]] internal method.

Note 2

[[Construct]] for proxy objects enforces the following invariants:

  • The result of [[Construct]] must be an Object.

10.5.14 ProxyCreate ( target, handler )

The abstract operation ProxyCreate takes arguments target and handler. It is used to specify the creation of new Proxy exotic objects. It performs the following steps when called:

  1. If Type(target) is not Object, throw a TypeError exception.
  2. If Type(handler) is not Object, throw a TypeError exception.
  3. Let P be ! MakeBasicObject(« [[ProxyHandler]], [[ProxyTarget]] »).
  4. Set P's essential internal methods, except for [[Call]] and [[Construct]], to the definitions specified in 10.5.
  5. If IsCallable(target) is true, then
    1. Set P.[[Call]] as specified in 10.5.12.
    2. If IsConstructor(target) is true, then
      1. Set P.[[Construct]] as specified in 10.5.13.
  6. Set P.[[ProxyTarget]] to target.
  7. Set P.[[ProxyHandler]] to handler.
  8. Return P.