为什么这个可配置的属性不可删除?

Why is this configurable property not deletable?

可配置的属性似乎是可删除的:

var o = {};
Object.defineProperty(o, 'prop', {
    configurable: true,
    value: 'val'
});
delete o.prop; // true
o.prop;        // undefined

但它在以下情况下不起作用,至少在 Firefox 和 Chrome 上:

var form = document.createElement('form'),
    input = document.createElement('input');
form.appendChild(input);
var elems = form.elements;
Object.getOwnPropertyDescriptor(form, 0)
      .configurable; // true <────────────────────── !!!
delete elems[0];     // false                         │
elems[0];            // input                         │
(function(){ 'use strict'; //                         V
    delete elems[0]; // TypeError: property 0 is non-configurable
})();                // and can't be deleted

但这似乎与规范相矛盾。

delete运算符定义如下:

11.4.1 - The delete Operator

The production UnaryExpression : delete UnaryExpression is evaluated as follows:

所以使用delete的结果取决于[[Delete]]。现在让我们看看 [[Delete]] 做了什么:

8.12.7 - [[Delete]] (P, Throw)

When the [[Delete]] internal method of O is called with property name P and the Boolean flag Throw, the following steps are taken:

  • Let desc be the result of calling the [[GetOwnProperty]] internal method of O with property name P.
  • If desc is undefined, then return true.
  • If desc.[[Configurable]] is true, then
    • Remove the own property with name P from O.
    • Return true.
  • Else if Throw, then throw a TypeError exception.
  • Return false.

因此,如果属性是可配置的,它应该是可删除的。

但是等等,也许 Object.getOwnPropertyDescritor 是一个巨魔并说 属性 是可配置的,但 [[Configurable]] 是 false。让我们看看:

15.2.3.3 - Object.getOwnPropertyDescriptor ( O, P )

When the getOwnPropertyDescriptor function is called, the following steps are taken:

  • If Type(O) is not Object throw a TypeError exception.
  • Let name be ToString(P).
  • Let desc be the result of calling the [[GetOwnProperty]] internal method of O with argument name.
  • Return the result of calling FromPropertyDescriptor(desc).

所以它也像[[Delete]]一样使用了[[GetOwnProperty]]。也许巨魔是 FromPropertyDescriptor?

8.10.4 FromPropertyDescriptor ( Desc )

When the abstract operation FromPropertyDescriptor is called with property descriptor Desc, the following steps are taken:

  • If Desc is undefined, then return undefined.
  • Let obj be the result of creating a new object as if by the expression new Object() where Object is the standard built-in constructor with that name.
  • ...
  • Call the [[DefineOwnProperty]] internal method of obj with arguments "configurable", Property Descriptor {[[Value]]: Desc.[[Configurable]], [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true}, and false.
  • Return obj.

所以不,它也不是巨魔。 属性 描述符的 configurable 属性 设置为 [[Configurable]] 值。

那么,一个可配置的属性怎么可能不能被删除呢?

实际上,可配置属性是可删除的。

但是有一个大问题:那只适用于native objects, but not to host objects

8.6.2 - Object Internal Properties and Methods

中所述

Host objects may support these internal properties with any implementation-dependent behaviour as long as it is consistent with the specific host object restrictions stated in this document.

对于那些,[[GetOwnProperty]] 必须表现不同:

If a property is described as a data property and it may return different values over time, then either or both of the [[Writable]] and [[Configurable]] attributes must be true even if no mechanism to change the value is exposed via the other internal methods.

在您的示例中,form.elements is a HTMLFormControlsCollection 实例由 HTML 规范定义,因此它是一个宿主对象。

因此,情况是

  • 它有一个自定义 [[GetOwnProperty]],表示 属性 '0' 是可配置的,因为它的值可能会改变。
  • 它还有一个自定义 [[Delete]],它不会删除 属性,即使 [[GetOwnProperty]] 说它是可配置的。