Object.isExtensible() 为 true,但 Object.defineProperty() 会抛出.为

Object.isExtensible() is true, but Object.defineProperty() throws. Why?

本文关键字:Object defineProperty true isExtensible      更新时间:2023-09-26

在Firefox和Safari中,我看到了Object.isExtensible()为真的情况,我可以添加一个具有常规属性赋值的新属性(即o.x = y(,但是Object.defineProperty()抛出"TypeError: Object.defineProperty(...不可扩展"。

这种情况发生在类型化数组中,也许其他类型的数组也是如此。我尝试使用isExtensible()来确保我不尝试在对象上定义属性。

我可以尝试/捕获定义属性,但我想了解这里发生了什么。有什么想法吗?

下面是一个关于jsfiddle的例子:http://jsfiddle.net/justinfagnani/qvgnk/

和代码:

function addProperty(o, name, value) {
  if (Object.isExtensible(o)) {
    Object.defineProperty(o, name, {'value': value});
    return true;
  }
  return false;
}
console.log(addProperty(new Date(), 'foo', 1));
console.log(addProperty(new ArrayBuffer(), 'foo', 1));

这至少应该为每个调用打印 true 或 false。在Firefox中,它会抛出ArrayBuffer。

注意:类型化数组现在在 ES6/ES2015 规范中,其中 ArrayBuffer 实际上是可扩展的。此答案仅供事先参考。


类型数组规范没有明确说明这一点,尽管它使用 Web IDL 来描述创建的对象,其中说:

除非另有指定,否则本节中定义的对象的 [[可扩展]] 内部属性的值为 true

我不确定他们为什么把它放在规范中,但他们可能选择使[[Extensible]]成为现实的一些原因,即使它不可扩展,包括:

  • 想要允许来自特定函数/路径的扩展(当它为 false 时禁止(
  • 保留允许扩展主机对象的可能性(禁止从 false 更改为 true(

根据 ECMAScript 规范(JavaScript 的"标准"版本(,主机对象在设置变量时是否抛出取决于实现 (§8.6.2(:

除非另有说明,否则主机对象可以以任何方式实现这些内部方法;例如,一种可能性是特定主机对象的[[Get]][[Put]]确实获取和存储属性值,但[[HasProperty]]总是生成 false。但是,如果实现不支持对主机对象内部属性的任何指定操作,则该操作在尝试时必须引发TypeError异常。

规范给出的所有不变量都不适用于这种情况,因此这在技术上是合法的。


需要澄清的注意事项:即使[[Extensible]]为 true,主机对象的内部函数也不必允许扩展对象。 该规范仅要求在 [[Extensible]] 为 false 时不扩展主机对象(参考(:

如果 ECMAScript 代码观察到主机对象的[[Extensible]]内部属性为 false,则主机对象的[[DefineOwnProperty]]内部方法不得允许向主机对象添加新属性。

Table 8/9 (§8.6.2( 中规范中给出的描述本身具有误导性,因为它们只涉及本机 ECMAScript 对象,而不涉及主机对象 - 这在表 8 前面的段落中特别指出,主机对象不受这些表中描述的属性的实现的约束。

我相信

这种行为依赖于严格模式和JavaScript引擎。

在 V8 (Chrome( 中,如果严格模式处于关闭状态,o.x = y 将成功,但不会向 o.x 分配任何内容,而Object.defineProperty始终抛出异常,而不管严格模式如何。

在严格模式下o.x将引发异常。

在 Rhino 中,Object.defineProperty 会抛出异常,o.x = y会静默失败。

以下是一些示例:

/usr/local/Cellar/tomcat6/6.0.37 >rhino -strict
Rhino 1.7 release 4 2012 06 18
js> var o = {a: 132};
js> Object.freeze(o);
[object Object]
js> Object.isExtensible(o);
false
js> o.x = 789;
789
js> o.x;
js> Object.defineProperty(o,'name',{value: 789})
js: uncaught JavaScript runtime exception: TypeError: Cannot add properties to this object because extensible is false.

在 V8 中

/usr/local/Cellar/tomcat6/6.0.37 >node
> var o = {a: 123}
undefined
> Object.freeze(o)
{ a: 123 }   
> Object.isExtensible(o)
false
> o.x = 13
13
> o.x
undefined
> Object.defineProperty(o,'name',{value:123})
TypeError: Cannot define property:name, object is not extensible.
    at Function.defineProperty (native)
    at repl:1:9
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
    at ReadStream.EventEmitter.emit (events.js:98:17)

在严格模式下

/usr/local/Cellar/tomcat6/6.0.37 >node --use_strict
> var o = {a: 123};
undefined
> Object.freeze(o);
{ a: 123 }
> Object.isExtensible(o);
false
> o.x = 13
TypeError: Can't add property x, object is not extensible
    at repl:1:6
    at REPLServer.self.eval (repl.js:110:21)
    at Interface.<anonymous> (repl.js:239:12)
    at Interface.EventEmitter.emit (events.js:95:17)
    at Interface._onLine (readline.js:202:10)
    at Interface._line (readline.js:531:8)
    at Interface._ttyWrite (readline.js:760:14)
    at ReadStream.onkeypress (readline.js:99:10)
    at ReadStream.EventEmitter.emit (events.js:98:17)
    at emitKey (readline.js:1095:12)