• 转:理解javascript中的delete机制(3)


    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

  • 相关阅读:
    ECMAScript——引用数据类型之date
    ECMAScript——引用数据类型之RegExp
    ECMAScript——引用数据类型之array
    ECMAScript——引用数据类型之object
    ECMAScript——基本数据类型之null和undefined
    ECMAScript——基本数据类型之boolean
    ECMAScript——基本数据类型之string
    ECMAScript——基本数据类型之number
    libgdx 3D Bullet 碰撞检测二
    libgdx 3D Bullet 碰撞检测一
  • 原文地址:https://www.cnblogs.com/philips/p/1737117.html
Copyright © 2020-2023  润新知