深入理解Delete(JavaScript)

 Delete  众所周知是删除对象中的属性. 但如果不深入了解delete的真正使用在项目中会出现非常严重的问题 (:

Following 是翻译  kangax 的一篇文章 "Understanding Delete";


PS:文章主要分为8部分, 有时间弄点瓜子儿, 整壶茶了解一下. (小编建议直接看原文地址, 以下翻译仅供自己学习使用);

相信大家如果有时间看完会有收获...也希望有大牛能指出其中翻译的不足...

目录:

§  原理

  § Type of Code (代码级别)

   § Execution context(执行上下文) 

   § Activation object / Variable object  

   § Property attributes(属性特性)

   § Build-ins and DontDelete(嵌入式/不可删除) 

   § Undeclared assignments (未声明任务)

§  FireBug confusion  (奇异的FireBug)

   § Deleting variables via eval(通过eval删除变量)

§  Browsers compliance (浏览器兼容性)

§  'delete' and host objects

§  ES5 strict mode 

§  Summary  

 

================Enein翻译===================

先上例子:

>>> var sum = function(a, b) {return a + b;}
>>> var add = sum;
>>> delete sum
true
>>> typeof sum;
"undefined"

忽略几个丢失分号. 这段代码你能看出什么问题? 

当然这个问题很明显 "delete sum" 是不会成功的. delete 返回的值不应该是 "true" , "typeof sum" 返回的结果也不是"undefined"。造成问题的原因是 在JavaScript中"delete 是不可以删除变量的".

这个例子有问题? 排版问题? 是个变相题? 应该都不是. 上面的代码会在FireBug Console下正确输出.(你可以快速测试一下) 仿佛在FireBug下有它自己的删除规则. 这...给我干蒙了. 到底是怎么回事? 我们来讨论一下.

要想知道答案我们首先要先知道 "delete" 操作符在JavaScript中的实际是怎样工作的: (主要从3个方向 什么情况能正确删除, 什么时候不能删除, 为什么);

让我们带着疑问继续往下看:(I’ll try to explain this in details

我们来看一个FireBug的古怪行为.并了解其实这是正常的.

我们将深入了解下 "声明变量","函数","加入属性"是怎么工作的并在适当的时候删除它们.我们还会看一下浏览器的兼容性以及其一些常见的Bugs, ECMAScript 5 strict mode 和如何改变delete 操作符的行为

注释: 在这里我将使用JavaScript和ECMAScript(这是真正意义上的ECMAScript除非有明确声明为Mozilla's ECMAScript扩展)

PS:这一段是作者对Mozilla MSNMSDN 上的两篇文章发表的个人看法(他会认为practically useless)这里不做翻译有兴趣的同学可以点其链接自行查看分析.

 

§ 原理

  为什么它能删除对象的属性

var o = { x: 1 };
delete o.x; // true
o.x; // undefined

  变量却不能, like this:

var x = 1;
delete x; // false
x; // 1

  函数也不允许, like this:

function x(){}
delete x; // false
typeof x; // "function"

注意当 属性不能被删除的时候将返回 false 

要明白理解这些, 要需要进一步理解变量实例概念、属性的特性。(有限的是在JavaScript相关书籍中涉及的知识还是比较少的)以下就要详细的介绍.

(如果你不关心这些东西为什么工作方式是这样的,那就skip this chapter)

 

§ Type of Code (代码级别) [ps:代码级别是出于自己的理解]

  在ECMAScript中有3种作用域: Global code(全局作用域), Function code(函数作用域), Eval code(Eval作用域) 以下对三种级别的描述.

  Global code :  当一段文本做为一个程序的时候, 它是在全局作用域下执行的. 在浏览器环境中通常写在SCRIPT标签下的内容会被解析, 因为也算是全局作用域

  Function code : 任何东西在function里是会随着function执行并执行.很明显这是属于函数作用域, 在浏览器中事件属性通过也会被当作函数作用域.(e.g <p οnclick=""/>)

  Eval code : 最后,  在eval函数体里的代码就是 Eval作用域.很快我们就会看到为什么这个类型是特殊的.

 

§ Execution context(执行上下文)  

  当ECMAScript的代码执行的时候, 它就一直在某一个执行上下文中,  “Execution context 是一个抽象的实体” 它会使我们明白作用或和变量实例化的过程. 对以上中种类型的范围, 它们就是一个执行上下文.

当function被执行的时候, 这个实体的上下文就是"Function code", 当代码是在Global code下被执行的时候, 那么它就是 "Global code" , 也 so on. 

  就你像我们看到的那样, 执行上下文逻辑上属于一个 stack (栈) 首先它可能是执行在全局作用域下的, 它拥有自己的上下文, 在这段代码里, 可能还会调用一个function, 这个function也会有自己的上下文, 在这个function里有可能还会调用一个function, function还可以递归调用, 以此类推.

 

§ Activation object / Variable object   

  每个执行上下文都会和一个("Variable Object")可变的对象相关联, 和执行上下文类型类似, Variable Object 也是一个抽象的实体. 通过一种机制来描述变量初始化过程, 现在, 我们感兴趣的是 变量和函数声明的时候 实际上是作为 Variable Object 的属性被加入的.

  当这个实体的执行上下文为 全局作用域的时候, 那么这个全局的对象会当做一个 "Variable Object" 这也就说明了, 为什么变量和函数的声明为全局的时候会 变成全局对象的属性了.

/* remember that `this` refers to global object when in global scope */
var GLOBAL_OBJECT = this;

var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true

function bar(){}
typeof GLOBAL_OBJECT.bar; // "function"
GLOBAL_OBJECT.bar === bar; // true

OK, 所以 全局变量 会变成 全局对象 的属性, 但对于局部变量它发生了什么, 在函数体内他们是怎么声明的, 很简单、它们变成了(Variable Object)变量对象的属性. 只是在作为Function code的时候有所不同, 一个变量对象不是一个全局对象. 但它会调用一个"Activation object"(激活对象), 每一次给函数分配上下文的时候Activation object将会被创建.

  不仅仅是变量和函数的声明会成为Activation object的属性, 函数的形参(形式参数:对应实际参数)和特殊对象Arguments object 注意 Activation object是一种内部机制, 是永远不可能访问的程序代码.

(function(foo){
var bar = 2; function baz(){} /* In abstract terms, Special `arguments` object becomes a property of containing function's Activation object: ACTIVATION_OBJECT.arguments; // Arguments object ...as well as argument `foo`: ACTIVATION_OBJECT.foo; // 1 ...as well as variable `bar`: ACTIVATION_OBJECT.bar; // 2 ...as well as function declared locally: typeof ACTIVATION_OBJECT.baz; // "function" */ })(1);

最后, 在Eval code中的变量声明是作为 创建变量对象上下文调用时的属性  Eval code 简单的使用变量对象的执行上下文, 代码执行是这样的:

var GLOBAL_OBJECT = this;


/*'foo' 被创建为一个变量对象调用上下文的属性, 在这个案例中它是全局对象*/ eval('var foo = 1;'); GLOBAL_OBJECT.foo; // 1 (function(){ /* 'bar' 被创建作为变量对象调用时上下文中的属性, 在案例中是function链中的激活对象*/ eval('var bar = 1;'); /* In abstract terms, ACTIVATION_OBJECT.bar; // 1 */ })();

§ Property attributes(属性特性)  

  我们就快要明白了, 现在我们清楚的明白变量到底发生了什么(它们属性之间的变换), 剩下 Property attributes了.每个属性都会存在0个或多个属性包括(ReadOnly(只读),DontEnum(不可枚举), DontDelete(不可删除)) 对于今天的话题我们只讨论DontDelete.

  当声明变量和函数变成变量对象的属性或者一个激活对象(作为一个Function code), 或者全局对象(Global code), 这些属性被创建并含有DontDelete特性.无论怎么样, 一些显式(隐式)属性分配上也会创建不含有Dontdelete的属性 为什么有的能有的不能:

 

var GLOBAL_OBJECT = this;

 /* 'foo' 是全局对象属性 它被创建通过变量声明所以它存在DontDelete特性
      这就是它为什么不会被删除
*/

  var foo = 1;
  delete foo; // false
  typeof foo; // "number"

 /* 'bar' 是一个全局对象的属性
      它被创建通过函数声明所以它存在DontDelete属性
      这也就是它为什么也删除不了
*/

  function bar(){}
  delete bar; // false
  typeof bar; // "function"

 /* ‘baz’ 也是全局对象的属性
      它的创建是通过分配属性所以它没有DontDelete是可以删除的
*/

  GLOBAL_OBJECT.baz = 'blah';
  delete GLOBAL_OBJECT.baz; // true
  typeof GLOBAL_OBJECT.baz; // "undefined"

 

§ Build-ins and DontDelete(嵌入式和不可删除) 

  这节我们说的是, 属性的一些特殊特性来控制这些属性可否被删除(注意: 一些内置的属性会被默认指定成DontDelete, 固不能被删除)特殊arguments变量(现在我们知道它是激活对象的属性)有DontDelete. 同样的一些function实例的length属性也存在DontDelete:

(function(){

    /* 不能删除 'arguments', 它是不可删除的*/
    delete arguments; // false
    typeof arguments; // "object"

    /* 不能删除function的lenth属性, 它也是不可删除的 */
    function f(){}
    delete f.length; // false
    typeof f.length; // "number"

})();

同样, 函数的形参也是有DontDelete的也是不可删除的.

(function(foo, bar){

    delete foo; // false
    foo; // 1

    delete bar; // false
    bar; // 'blah'

})(1, 'blah');

§ Undeclared assignments (未声明的任务)

  未声明的任务创建一个全局对象的属性. 除非在全局对象之前你能找到这个属性是属性哪个作用域链的. 现在我们清楚,属性任务和变量声明之间的不同, 后者是是DontDelete属性, 前者则不是(它应该清楚为什么未被声明的会创建不含有DontDelete的属性).

 var GLOBAL_OBJECT = this;

 /* 创建全局属性通过变量声明; 属性不可删除的*/
  var foo = 1;

 /* 创建全局属性通过未声明的任务 其属性是可删除的 */
  bar = 2;

  delete foo; // false
  typeof foo; // "number"

  delete bar; // true
  typeof bar; // "undefined"

注意在属性创建期间, 其属性是被确定的. 后面的任务是不可改变已存在的属性的, 明白这点是很重要的.

/* 'foo' 作为含有DontDelete的属性被创建 */
  function foo(){}

/* 之后的任务不能修改其属性, DontDelete还在 */
  foo = 1;
  delete foo; // false
  typeof foo; // "number"

 /* 但加入属性是新的, 就不含有DontDelete. */

  this.bar = 1;
  delete bar; // true
  typeof bar; // "undefined"

§ FireBug confusion  (奇异的FireBug)

  在FireBug发生了什么? 之前说过在FireBug console中变量的声明是可以删除的. 这违背了我们之前说的所有? 好吧, 之前我说过, Eval code的变量声明时有着特殊的行为, 变量声明在Eval code里实际上是创建了没有DontDelete的属性:

 eval('var foo = 1;');
 foo; // 1
 delete foo; // true
 typeof foo; // "undefined"

同样对于在Function code里调用:

(function(){
    eval('var foo = 1;');
    foo; // 1
    delete foo; // true
    typeof foo; // "undefined"
})();

这就是重点, 所有在Firebug console中执行的代码会被当成是 Eval code来进行解析  所以

和console的不同.

  

§ Deleting variables via eval(通过eval删除变量) 

  最有意思的是eval的特性, 另一方面ECMAScript能从技术上允许我们去删除不可删除的属性.在同一个上下文中function的声明是可以被同名变量重写的.

 function x(){ }
 var x;
 typeof x; // "function"

注意 function声明优先,重写同名变量(或者, 换句话说, 在变量对象中存在了相同属性). 这是因为 函数声明被实例是在变量声名(Variable declarations)之后, 是允许被覆盖的不仅函数声明替换这前属性的值, 它也能替换它的属性.

如果我们通过eval来声明function那么还是可以替换相应的属性, 因为在eval里创建的变量声明没有DontDelete, 以下示例会从本质上删除存在的DontDelete特性。

 

  var x = 1;

  /* Can't delete, `x` has DontDelete */

  delete x; // false
  typeof x; // "number"

  eval('function x(){}');

  /* `x` property now references function, and should have no DontDelete */

  typeof x; // "function"
  delete x; // should be `true`
  typeof x; // should be "undefined"

不幸的事,  我尝试各种不能工作的场景, 有可能我会有所疏漏.

 

§ Browsers compliance (浏览器兼容性)

   学习这些东西的工作原理是很实用的, 实践至上. 在浏览器兼容上会存在多在的差异.作者做了很多的浏览器测试, 最主要的是属性中含有DontDelete是不可删除的,相反则然.

当今浏览器的脾气都是很友好的. 我测试的Opera 7.54+, Firefox 1.0+, Safari 3.1.2+, Chrome 4+浏览器都是可行的.

Safari 2.x and 3.0.4 是有问题的对于function的参数问题上;这些参数被看做没有DontDelete特性, 问题主要是我们可以detele它们. 实际上Safari 2.x存在更多的问题(删除没有引用的变量e.g delete 1)会抛异常, function的声明会创建可删除属性(不包括变量声名), 变量声明在eval中变为不可删除(除了function声明).

   Konqueror (3.5)也同样(删除function参数会报错)

Gecko DontDelete Bug 

  Gecko 1.8.x browsers — Firefox 2.x, Camino 1.x, Seamonkey 1.x, etc. 显示出一个很有意思的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)

比较出乎意外的是IE 5.5 - 8 基本测试都通过了. 只是删除没有引用的会报错(e.g delete 1) , 但其实实际上IE中有更严重的BUG是关于全局对象的.

IE BUGS(IE bug) 

  这一章主要是说一下Internat Explorer下的BUGS:

  在IE5.5-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删除也会报错.

但说的也不全部, 显示的创建一个属性在删除的时候是一直会报错的 

    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"

但你要尝试使用全局对象的属性的方式来删除, 则会报错:

   x = 1;
    delete this.x; // TypeError: Cannot delete 'this.x'

小结: 只要是 delete this.x 都不会成功.

Misconceptions (歧义) 

/*...*/

§ 'delete' and host objects

简单的推算一下delete如下:

  • 如果这个运算对象没有引用, 返回 true.
  • 如果不是Object的内部属性, 返回 true.
  • 如果Object有属性但有DontDelete特性, 返回 false.
  • 其它移除属性返回 true.

无论怎么, delete操作符在宿主对象上的行为也是不可预知的 这是其实是没有问题的, 宿主对象是允许(通过规范)去实现各种操作行为比如 read(内部实现[[Get]]方法), write(内部实现[[Put]]方法), delete(内部实现[[Delete]]方法).

之前我们已经讨论了IE的差异, delete 某一对象会抛异常, 在一些火狐版本中删除 window.location 抛出的异常, 你是不能相信delete 宿主对象的属性的返回值的. 看下面代码在FireFox:

/* "alert" is a direct property of `window` (if we were to believe `hasOwnProperty`) */
window.hasOwnProperty('alert'); // true

delete window.alert; // true
typeof window.alert; // "function"

 删除window.alert返回的是true , 它的解析过程 :

  One step : 被解析成一个引用(不会返回true);

  two step : 是window的内部属性(不会返回true);

  只有在真正 "delete window.alert" 的时候才真正删除了嘛? no 它还是没有被删除.

小结: 从来不要相信宿主对象

 

§ ES5 strict mode  

  ECMAScript 规范 严格格式下会有很多限制, 在这几种情况下会报语法错误: delete 直接去删除 变量, 函数的参数, 函数定义, 另外, 当属性有内部属性[[Configurable]] == false 是会报 类型错误:

(function(foo){

    "use strict"; // enable strict mode within this function

    var bar;
    function baz(){}

    delete foo; // SyntaxError (when deleting argument)
    delete bar; // SyntaxError (when deleting variable)
    delete baz; // SyntaxError (when deleting variable created with function declaration)

    /* `length` of function instances has { [[Configurable]] : false } */

    delete (function(){}).length; // TypeError

})();

 另外, 在删除未声明变量(或未指明的引用)抛出词法错误:

"use strict";
delete i_dont_exist; // SyntaxError 

 同样的在未指定确定类型的变量在严格模式(strict mode)下也是会报词法错误的:

"use strict";
i_dont_exist = 1; // ReferenceError

 现在我明白了, 在严格模式下的这些限制都是很有用的, ECMAScript strict mode 解决了很多问题, 而不是忽视它们, 从中我们也可以通过ECMAScript这些限制, 反向理解来学习更深入的知识. 

§ Summary  

  这篇文章说的太长了, 如果你能静下心来好好看完, 那你将会明白很多, 这里我只是说了一部分关于Array的delete我这里就不说了, 但希望有兴趣的同学可以自己去尝试(你可以参考MDC  for that particular explanation 文章).

   这是里简单的做一下在JavaScript中delete的操作:

  • 变量和函数声明属性要么是激活对象, 要么是全局对象

  • 属性里有DontDelete特性的表示不可删除属性.
  • 变量和函数声明只要是在"全局代码块"/"函数级代码块"中都会有 —— DontDelete.

  • Functions的参数也是属于激活对象的属性, 所以也有 —— DontDelete.
  • 变量和函数声明在Eval代码块中的, 都不会创建 —— DontDelete.

  • 为对象加入新的属性(没有任何特性), 也是不会创建 —— DontDelete.
  • 不管他们想怎样, 宿主对象对删除是会返回状态的.

 ================Enein翻译===================

转载于:https://www.cnblogs.com/enein/archive/2012/08/23/2651312.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整全套资源下载地址:https://download.csdn.net/download/qq_27595745/70761177 【完整课程列表】 完整版Java web开发教程PPT课件 Java开发进阶教程 第01章 JavaScript简介以及基本语法(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第02章 Dom模型,控制客户端元素(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第03章 表单验证,常用函数(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第04章 自定义web服务器(共14页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第06章 SERVLET(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第07章 JSP语法及运行过程(共13页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第08章 JSP内置对象(共22页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第09章 jstl、el、java bean(共18页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第10章 过滤器、监听器、自定义标签(共19页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第11章 AJAX实现(共11页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第12章 自定义mvc框架(共11页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第13章 spring ioc aop(共18页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第14章 spring mvc介绍,原理以及搭建(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第15章 spring mvc核心对象拦截器(共26页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第17章 自定义orm框架(共11页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第18章 mybatis框架的搭建以及应用(共13页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第19章 mybatis高级应用(共21页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第20章 数据库连接池,缓存(共15页).pptx 完整版Java web开发教程PPT课件 Java开发进阶教程 第22章 常用框架的介绍以及环境搭建(共16页).pptx JS课程案例ebookHTML\网上书店需求文档.doc

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值