删除Javascript Object中间的key(5)
Safari 2.x 和 3.0.4在删除函数参数时有问题; 这些属性看起来是不带DontDelete被创建的, 所以可以删除它们. Safari 2.x有更多的问题——删除非引用类型变量(如: delete 1)会抛出异常; 函数声明会创建可删除的属性(但是, 奇怪的是, 变量声明却不会); eval中的变量声明会变成不可删除的(但是函数声明是可删除的).
跟Safari类似, Konqueror(3.5, 不是4.3)会在删除非引用类型时抛出异常(如: delete 1), 并且错误地让函数变量变为可删除的.
译者注:
我测试了最新版本的chrome和firefox以及IE, 基本还是保留在除23,24会fail其它均pass的情况. 同时测试了UC和一些手机浏览器, 除了诺基亚E72的自带浏览器还会Fail 15,16之外, 其余的自带浏览器大都与桌面浏览器效果一样. 但值得一提的是, Blackberry Curve 8310/8900的自带浏览器可以pass测试23, 令我很惊讶.
Gecko DontDelete bug:
Gecko 1.8.x 浏览器 —— Firefox 2.x, Camino 1.x, Seamonkey 1.x等等. —— 表现出了一个非常有趣的bug, 对属性的显式赋值会删除它的DontDelete特性, 即使这个属性是通过变量声明或函数声明创造的.
function foo(){}
delete foo; // false (as expected)
typeof foo; // "function" (as expected)
/* now assign to a property explicitly */
this.foo = 1; // erroneously clears DontDelete attribute
delete foo; // true
typeof foo; // "undefined"
/* note that this doesn't happen when assigning property implicitly */
function bar(){}
bar = 1;
delete bar; // false
typeof bar; // "number" (although assignment replaced property)
令人吃惊的是, Internet Explorer 5.5 - 8 通过了完整的测试集, 除了删除非引用类型(如: delete 1)会抛出异常(就像旧的Safari一样). 但是在IE下有更严重的bugs, 它不是那么明显. 这些bugs跟Global object有关.
IE bugs:
这整章都在说Internet Explorer的bugs? 哇! 真是令人吃惊!
在IE中(至少是IE 6-8), 以下表达式会抛出异常(当在全局代码中执行时):
this.x = 1;
delete x; // TypeError: Object doesn't support this action
这一个也会, 但是会抛出不同的异常, 这使得事情更有趣了:
var x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
这看上去好像是在IE中, 全局代码中的变量声明没有在全局对象上创建属性. 通过赋值来创建属性(this.x = 1)和之后通过delete x来删除它会抛出错误. 通过声明来创建属性(var x = 1)并且在之后通过delete this.x来删除它会抛出另一个错误.
但这还不是全部. 通过显式赋值来创建属性事实上总会引起在删除时的抛出异常. 这里不止有错误, 而且所创建的属性似乎会拥有DontDelete特性, 而这当然是不应该具有的.
this.x = 1;
delete this.x; // TypeError: Object doesn't support this action
typeof x; // "number" (still exists, wasn't deleted as it should have been!)
delete x; // TypeError: Object doesn't support this action
typeof x; // "number" (wasn't deleted again)
现在, 我们会认为 在IE下, 未声明的赋值(应当在全局对象上创建属性)确实会创建可删除的属性.
x = 1;
delete x; // true
typeof x; // "undefined"
但是, 如果你是同通过全局代码中的this引用来删除这个属性的话(delete this.x), 就会弹出一个类似的错误.
x = 1;
delete this.x; // TypeError: Cannot delete 'this.x'
如果我们想要归纳一下这种行为的话, 看起来是从全局代码中使用delete this.x来删除变量从来不可能成功. 当问题中的属性通过显式的赋值(this.x = 1)来创建时,delete抛出了一个错误; 当属性是通过未声明的赋值(x = 1)或通过声明(var x = 1)来创建时, delete抛出另外一个错误.
delete x, 另一方面来说, 应当只在属性是通过显式赋值来创建时抛出错误 ——this.x = 1.如果一个属性是通过声明来创建的(var x = 1), 删除操作从来不会发生, 并且删除操作会正确地返回false. 如果一个属性是通过未声明的赋值来创建的(x = 1), 删除操作会像期望地一样工作.
我这9月份又思考了一下这个问题, Garrett Smith建议说在IE下,
"全局变量对象(The global variable object)是实现为一个JScript对象的, 并且全局对象是由host来实现的".
Garrett使用了Eric Lippert's blog entry作为参考.
我们多多少少可以通过实施一些测试来确认这个理论. 注意到this和window看起来是应当指向同一个对象的(如果我们能够信任===操作符的话), 但是变量对象(函数声明所在的那个对象)却与this所指向的不同.