to be a guide ,to be an elegant programmer

做一个优雅的码农!

博客园 首页 新随笔 联系 订阅 管理

IE bugs

整个章节仅仅为了IE中的bug,想不到吧!

在IE浏览器中(至少是IE6-IE8),下面的表达式抛出错误(在全局代码中执行):

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特性,这当然不应该有:

代码
1 this.x = 1
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中创建了可删除属性

= 1
delete x; // true 
typeof x; // "undefined"

但是,如果您尝试通过“this”引用在全局代码中删除它(delete this.x ),一个熟悉的错误弹出:

= 1
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 ,我们可以通过一些测试验证这些理论。请注意,thiswindow似乎引用同一对象(如果我们相信“===”运算符),但可变对象(在一个声明的函数中的对象)不同于这一点。

 

代码
1 /* in Global code */
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 的算法大概是这样:

  1. 如果操作不是一个引用,返回true
  2. 如果一个对象没有直接的属性,返回true;(我们知道,对象可以是激活对象,可以是全局象);
  3. 如果一个属性存在并有DontDelete特性,返回false
  4. 否则,删除属性并返回true

但是,宿主对象的delete 运算符的行为难以预测。实际上并没有错:除了少数几个,宿主对象是允许执行任何类型的运算行为的(按规范),如read(内部的[get]方法)、write(内部的[put]方法)或delete(内部的[delete]方法)。这个定制的[[Delete]]行为使得宿主对象如此混乱。

在IE中我们已经看到一些古怪的行为,如果删除某些对象(明显作为宿主对象来执行)将抛出错误。Firefox的一些版本在尝试删除window.location时将抛出错误。当涉及到宿主对象时,你不能信任delete返回的任何值。看看在Firefox会有什么发生:

1 /* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */ 
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。

 

代码
 1 (function(foo){ 
 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。

"use strict"
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

 

posted on 2010-05-17 10:16  老苏  阅读(324)  评论(0编辑  收藏  举报