Skip to content

DELPROP: exposed property deletion algorithm ("delete" operator)

Background

Properties are deleted in ECMAScript code with the delete operator, e.g.:

delete foo.bar;

This involves:

  • A property accessor reference (E5 Section 11.2.1)
  • delete semantics (E5 Section 11.4.1)
  • A call to [[Delete]]

The property accessor coercions are the same as for GetValue:

  • The base reference is checked with CheckObjectCoercible()
  • The property name is coerced to a string

The delete expression will then:

  • Coerce the base value to an object
  • Call the [[Delete]] algorithm

Note that if the base value is not an object, a temporary object will be created by coercion. Since a deletion always operates on the "own properties" of an object, the deletion can only have side effects (error throwing) side effects. Any other effects will be lost with the temporary object. This is discussed in more detail below, for the deletion algorithm.

Notes:

  • [[Delete]] only checks for the property P in the original object O, and does not follow the prototype chain
  • In particular, an inherited property P which would prevent a [[Put]] does not affect the outcome of [[Delete]]

First draft

Starting from the property accessor, then applying delete (and skipping any unused steps):

  1. Call CheckObjectCoercible for the base value. In practice, throw a TypeError if the base value is null or undefined.
  2. Coerce property name to string using ToString().
  3. Coerce base value to object using ToObject() and call [[Delete]] with the coerced object, the coerced key, and a "Throw" flag set if the property reference is contained in strict mode code.

More formally, suppose O is the base value, P is the property name value, and currStrict is true if the property deletion expression occurred in strict code:

  1. If O is null or undefined, throw a TypeError
  2. P = ToString(P)
  3. O = ToObject(O)
  4. Call O.[[Delete]](P, currStrict), and return its result

Avoiding object coercion

We want to avoid the object coercion; let's first make it more explicit:

  1. If O is null or undefined, throw a TypeError
  2. P = ToString(P)
  3. If O is an object, call [[Delete]](O, P, currStrict), and return its result
  4. Else O is primitive: a. O = ToObject(O) (create temporary object) b. Call O.[[Delete]](P, currStrict), and return its result

Avoiding temporary objects

Note that a [[Delete]] only operates on the "own properties" of the target object. When the base value is not an object, the deletion operates only on the temporary object. Since the temporary object is immediately discarded, there are only two possible user visible effects:

  • The return value of [[Delete]], which is:
    • true, if the property does not exist
    • true, if the property exists and could be deleted
    • false, if the property exists, cannot be deleted, and Throw is false (if Throw is true, an error is thrown instead)
  • Errors thrown by [[Delete]], which happens if:
    • The (own) property exists, the property is non-configurable, and the Throw flag is set, i.e. we're evaluating delete in strict code

The coerced temporary object can be:

  • a Boolean instance: no own properties
  • a Number instance: no own properties
  • a String instance: has length and array indices (inside string length) as own properties, all non-configurable

Given these, the algorithm can be changed to avoid creation of temporaries entirely:

  1. If O is null or undefined, throw a TypeError
  2. P = ToString(P)
  3. If O is an object, call [[Delete]](O, P, currStrict) and return its result
  4. Else O is primitive: a. If O is a boolean, return true b. If O is a number, return true c. If O is a string: 1. If P is length or an array index inside the O string length: a. If currStrict is true, throw a TypeError b. Else, return false 2. Else, return true d. Return true (This step should never be reached, as the checks above are comprehensive.)

Step 4 can be simplified a bit:

  1. If O is null or undefined, throw a TypeError
  2. P = ToString(P)
  3. If O is an object, call [[Delete]](O, P, currStrict) and return its result
  4. If O is a string: a. If P is length or an array index inside the O string length: 1. If currStrict is true, throw a TypeError 2. Else, return false
  5. Return true

Fast path for array indices

It would be straightforward to add a fast path for array indices, but there is no fast path in the current implementation for array index deletion. The index is always string coerced and interned.