Object.defineProperty 接口浏览器实现的bug.和疑惑


先看看 MSDN上的描述.  

Adds a property to an object, or modifies attributes of an existing property.

Object.defineProperty(object, propertyname, descriptor)

object

Required. The object on which to add or modify the property. This can be a native JavaScript object or a DOM object.

 (can be 不等于 must be啊. IE8到底搞什么?另外Safari并不允许DOM object.)

propertyname

Required. A string that contains the property name.

descriptor

Required. A JavaScript object that is a descriptor that describes the property. The definition can be for a data property or an accessor property.

The object that was passed to the function.

You can use the

 Object.defineProperty function to do the following:

  • Add a new property to an object. This occurs when the object does not have the specified property name.

  • Modify attributes of an existing property. This occurs when the object already has the specified property name.(参考后面的注释1)

The property definition is provided in a descriptor object, which describes the attributes of a data property or an accessor property. The descriptor object is a parameter of theObject.defineProperty function.

descriptor 参数: (参考 ECMA262 Edition5 8.12.9章节)


Data descriptor attribute

                                     Description

Default if not specified when you add a property

value

The current value of the property.

undefined

writable

true or false. If writable is set to true, the property value can be changed.

false

enumerable

true or false. If enumerable is set to true, the property can be enumerated by a for…in statement.

false

configurable

true or false. If configurable is set to true, property attributes can be changed, and the property can be deleted.

false




BUG:


1. IE8,普通的 new Object 无法使用此接口.

var o = {};
Object.defineProperty(o,'abc',{value:123});
 //抛出异常,不是因为别的.仅仅因为 o是一个普通的js object.
o.abc = 300
alert(o.abc);

而如果 o = window 或一些 DOM Element 等宿主对象 就可以使用此接口.但并不是所有宿主对象都可以. 另一个重要的问题是. 对于Attributes参数缺省情况下,IE8也没有实现 o的abc属性的默认只读.即使,显式加上 writable:false.也如此.不知道这个接口被IE8实现成这个鸟样子,到底还有什么用.

2.IE8 Configurable:false


var o =window;
Object.defineProperty(o,'abc',{value:123,configurable:false});
delete o.a
 //IE8此处抛出异常.
alert(o.abc);

 也就是说.configurable 完全没有意义. 其现象就就如同一个固有的bug那样.参考代码:

window.o = 123;
delete window.o; // IE8 抛出异常. 同样是对象不支持此操作.



3. obj 为window宿主对象模拟 Const.

Object.defineProperty(window,'abc',{value:123,writable:false});
abc = 200;
window.abc = 300;
alert([abc,window.abc]) 
// ie8,Safari5 300,300  | Chrome,Ie9 123,123

这里就涉及一个很郁闷的问题.IE8 SUCKS,我可以理解但是Safari跟着凑什么热闹呢? 好在, 如果我想要的是模拟 Const 的话 Safari4 早就支持了. 所以我们可以假设IE9+ 可以用这个来模拟 Const 哦.
当然,如果window 换成一个普通的 object, Safari则无此问题.所以这应该是Safari 对 宿主对象实现此接口的一个bug.



注1 :  此问题我仍然有点迷惑.至少从ECMA62 Edition5中的相关定义.感觉所有浏览器的实现都没符合标准.或者是我对标准的理解有误.
参考代码 :
var o = {abc:123};
Object.defineProperty(o,'abc',{value:200,Writable:false}); // writable 而不是 Writable. 写成大写.就被认为特性是缺省的。然后 IsDataDescriptor 就会false.
o.abc = 300;
alert(o.abc)//支持defineProperty的浏览器都打印300. 即writable:false,失效了

对于 对象o,已经通过[[Put]]内部方法设置的属性来说(o.abc =123或例子中json方式),这个属性就是一个 data property .(区别于accessor property,setter getter属性.)其特性集(Writable,Configurable,Enumerable)都会被设为true. 而 {value:200,Writable:false} 这个descriptor object,因为具备value特性. 结果就是对他们俩 进行 IsDataDescriptor内部运算 的结果就将是, 两个就都是true.  而最终进入步骤 10.b(else, the [[Configurable]] field of current is true, so any change is acceptable. 参考8.12.9章节).也就是新的特性应该被应用上去.那么 测试代码的结果就让我感到迷惑了.
  而观察DefineOwnProperty的步骤.发现大概只有在进入 步骤 9.b.i 即. 即把abc这个data property 转换为了一个 accessor property.而特性操作就被无视了.  但是进入步骤9的条件是. abc属性的descriptor 和 {value:200,Writable:false}  至少有一个在做 IsDataDescriptor运算时返回false. 但是这一点,在测试代码中明显不可能. 所以我困惑无比. 
  我更希望是我对标准的理解有错误.而不是浏览器的实现都有问题.期望我能更早的找到这个问题的答案.
对于这个疑惑终于有了答案. hax,指出了我的悲剧. 我的 descriptor 的 特性名,首字母写成了大写.所以默认为忽略了. 所以 实际上就是进入了 9.b.i 了.  之前特性名都写成大写。之所以感觉上是生效的,是因为默认的都是false. 悲剧的人生啊... 留此贴纪念一下我的悲剧.

posted @ 2011-04-27 16:38  Franky  阅读(8162)  评论(5编辑  收藏  举报