4

在 Firefox 和 Safari 中,我看到了一种情况Object.isExtensible(),我可以添加一个具有常规属性分配的新属性(即o.x = y),但Object.defineProperty()会抛出“TypeError: Object.defineProperty(...) is not extensible”。

这发生在类型化数组上,也许其他类型也是如此。我正在尝试使用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。

4

2 回答 2

4

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


Typed Array 规范没有明确说明这一点,尽管它使用Web IDL来描述创建的对象,其中说明如下:

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

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

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

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

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

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


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

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

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

于 2013-10-12T00:50:15.810 回答
3

我相信这种行为取决于严格模式和 JavaScript 引擎。

在 V8 (Chrome) 中,如果关闭严格模式,o.x = y将成功,但不会为 分配任何内容o.x,而无论严格Object.defineProperty模式如何,都会抛出异常。

在严格模式下o.x会抛出异常。

在 RhinoObject.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)
于 2013-10-12T00:39:48.547 回答