Object.isExtensible()为true,但抛出Object.defineProperty()。为什么?

时间:2013-10-12 00:13:14

标签: javascript

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

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

我可以将defineProperty放在try / catch中,但我想了解这里发生了什么。有什么想法吗?

以下是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。

2 个答案:

答案 0 :(得分:4)

  

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


Typed Array specification没有明确说明这一点,尽管它使用Web IDL来描述创建的对象,其中包含以下内容:

  

除非另有说明,否则本节中定义的对象的[[Extensible]]内部属性的值为 true

我不确定为什么他们将这个放在规范中,但是为什么他们可能选择使[[Extensible]]为真,即使它不可扩展,也包括:

  • 希望允许来自特定功能/路径的扩展(当它为假时禁止)
  • 留出允许扩展主机对象的可能性(禁止从false变为true)

根据the ECMAScript specification(JavaScript的“标准”版本),主机对象是否抛出设置变量是依赖于实现的(§8.6.2):

  

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

规范中给出的不变量都不适用于此实例,因此这在技术上是合法的。


请注意:即使[[Extensible]]为真,主机对象的内部函数也不必允许对象进行扩展。如果[[Extensible]]为false(reference),则规范仅强制要求不扩展宿主对象:

  

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

表8/9(§8.6.2)中的规范中给出的描述本身就具有误导性,因为它们只涉及本机ECMAScript对象,而不是主机对象 - 这在表8之前的段落中具体说明,即主机对象不需要实现这些表中描述的属性。

答案 1 :(得分:3)

我相信这种行为取决于严格模式和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)