IE bugs
整个章节仅仅为了IE中的bug,想不到吧!
在IE浏览器中(至少是IE6-IE8),下面的表达式抛出错误(在全局代码中执行):
delete x; // TypeError: Object doesn't support this action
这个也是一样,但异常不同,只是更有趣:
delete this.x; // TypeError: Cannot delete 'this.x'
IE中看起来好像在全局代码中声明变量不能在全局对象中创建属性。通过赋值创建属性(
this.x = 1
),然后通过delete
删除x将抛出错误。通过声明创建创建属性(var x = 1
),然后通过delete this.x
删除将抛出另外一个错误。
但这还没完。实际上通过明确的赋值创建的属性在删除时始终引发错误。这不仅是一个错误,而且创建的属性似乎设置了DontDelete特性,这当然不应该有:
2 delete this.x; // TypeError: Object doesn't support this action
3 typeof x; // "number" (still exists, wasn't deleted as it should have been!)
4 delete x; // TypeError: Object doesn't support this action
5 typeof x; // "number" (wasn't deleted again)
与我们思考的相反,未声明的变量(应该在一个全局对象中创建属性)在IE中创建了可删除属性。
delete x; // true
typeof x; // "undefined"
但是,如果您尝试通过“this”引用在全局代码中删除它(delete
this.x ),一个熟悉的错误弹出:
delete this.x; // TypeError: Cannot delete 'this.x'
如果我们总结这些行为,从全局代码中
delete this.x
似乎是不成功的。当涉及到的属性是通过显式声明(this.x = 1
)来创建的,delete
将抛出一个错误。当属性是通过未声明的赋值(x = 1
)或声明(var x = 1
)来创建属性时,delete
将抛出另一个错误。
另一方面,当涉及到的属性是通过显式声明(this.x = 1
)创建时,delete
x 抛出错误。如果一个属性是通过声明(var x = 1
)来创建的,删除根本不会发生,并返回正确的false。如果属性是通过未声明的方式(x = 1
)创建,删除操作将按预期进行。
去年九月我正在思考这个问题,Garrett Smith 建议“在IE中全局可变对象作为一个JScript对象,全局对象有宿主执行”。Garrett 引用Eric Lippert’s blog entry ,我们可以通过一些测试验证这些理论。请注意,this
和window
似乎引用同一对象(如果我们相信“===
”运算符),但可变对象(在一个声明的函数中的对象)不同于这一点。
2 function getBase(){ return this; }
3
4 getBase() === this.getBase(); // false
5 this.getBase() === this.getBase(); // true
6 window.getBase() === this.getBase(); // true 7.window.getBase() === getBase(); // false
误区
理解事物为什么那么工作是一种难以言说的美,我在网上已经看到了与delete
运算符误解相关的误区。例如,在关于栈溢出的回答(评分出其不意的效果高)中,它自信的解释道:“delete is supposed to be no-op when target isn’t an object property ”。现在,我们已经理解了delete
行为的核心,很清楚这个答案是不准确的。delete
不区分变量和属性(事实上,对于删除,这些都是引用),真正的只关心的是DontDelete特性(和属性存在)。
非常有意思的看到这个误解如何相互影响,在同样一个线程中,有人首先提出要直接删除变量(除非它是在eval
中声明,否则不会生效),接着另外一个人提出一种错误的纠正方法--在全局中可能删除变量,但在函数内不行。
在网站上解释Javascript 最好小心,最好总是抓住问题的核心。
‘delete’和宿主对象
delete
的算法大概是这样:
- 如果操作不是一个引用,返回
true
; - 如果一个对象没有直接的属性,返回
true
;(我们知道,对象可以是激活对象,可以是全局象); - 如果一个属性存在并有DontDelete特性,返回
false
; - 否则,删除属性并返回
true
;
但是,宿主对象的delete
运算符的行为难以预测。实际上并没有错:除了少数几个,宿主对象是允许执行任何类型的运算行为的(按规范),如read(内部的[get]方法)、write(内部的[put]方法)或delete
(内部的[delete]方法)。这个定制的[[Delete]]行为使得宿主对象如此混乱。
在IE中我们已经看到一些古怪的行为,如果删除某些对象(明显作为宿主对象来执行)将抛出错误。Firefox的一些版本在尝试删除window.location
时将抛出错误。当涉及到宿主对象时,你不能信任delete
返回的任何值。看看在Firefox会有什么发生:
2 window.hasOwnProperty('alert'); // true
3 delete window.alert; // true
4 typeof window.alert; // "function"
删除window.alert
返回true
,虽然这个属性什么也没有,它应该导致这个结果。它保留了一个引用(因此在第一步中不应该返回true
),它是窗口对象的直接属性(因此第二步中不能返回true)。唯一的办法让delete
返回true
是在第四步之后真正删除属性。但是,属性是永远不会被删除的。
这个故事的寓意在于永远不要相信宿主对象
ES5严格模式
那么,ECMAScript 5th edition 的严格模式可以拿到台面上来了。一些限制正被引入,当delete
运算符是一个变量、函数参数或函数标识符的直接引用时将抛出SyntaxError。另外,如果属性内部有[[Configurable]] == false,将抛出TypeError。
2 "use strict"; // enable strict mode within this function
3 var bar;
4 function baz(){}
5 delete foo; // SyntaxError (when deleting argument)
6 delete bar; // SyntaxError (when deleting variable)
7 delete baz; // SyntaxError (when deleting variable created with function declaration)
8 /* `length` of function instances has { [[Configurable]] : false } */
9 delete (function(){}).length; // TypeError
10 })();
另外,删除未声明的变量(换句话说,没有找到的引用)也抛出SyntaxError。
delete i_dont_exist; // SyntaxError
正如你所理解的那样,考虑到删除变量、函数声明和参数会导致如此多得混淆,所有这些限制就有点意义。与不声不响的忽略删除行为相反,严格模式应该采取更积极的、更具有描述性的措施。
总结
这篇文章是冗长的,我打算去讨论用delete
删除数组选项和它的含义。你可以随时参考MDC 的文章了解具体的解释(或阅读规范,自己实验)。
这是Javascript中delete
运算符工作的简短概要:
- 变量和函数声明要么是激活对象的属性,要么是全局对象的属性;
- 属性有一些特性,其中之一就是DontDelete,它决定一个属性是否能删除;
- 在Global 和Function code 中的变量和函数声明总是有DontDelete特性;
- 函数参数也是激活对象的属性,具有DontDelete特性;
- 在Eval代码中的变量和函数声明总是创建没有DontDelete特性的属性;
- 新的属性总是带有空的特性(因此没有DontDelete特性);
- 宿主对象允许对删除作出反应,无论它们是否愿意如此;
如果你想了解更多这里这里描述的东西,请参阅ECMA-262 3rd edition specification。
我希望你喜欢这篇综述,并能学到新东西。任何疑问、建议、更正,一律欢迎。
相关阅读:
原文地址:Understanding delete
转载地址:http://www.denisdeng.com/?p=858