在javascript中,我们有时候要使用delete删除对象。但是,对于delete的一些细节我们未必尽知。昨天,看到kangax分析delete的文章,获益匪浅。本文将文章的精华部分翻译出来,与各位分享。
- 原理
- 代码类型
- 执行上下文
- 激活对象/可变对象
- 属性特性
- 内置对象和DontDelete
- 未声明的赋值
- Firebug 困惑
- 通过eval删除变量
- 浏览器兼容性
- Gecko DontDelete bug
- IE bugs
- 误区
- ‘delete’和宿主对象
- ES5严格模式
- 总结
原理
为什么我们能删除一个对象的属性?
1 var o = { x: 1 };
2 delete o.x; // true
3 o.x; // undefined
但是,像这样声明的变量则不行:
2 delete x; // false
3 x; // 1
4
5
或者如此声明的函数:
2 delete x; // false
3 typeof x; // "function"
4
5
注意,当一个属性不能被删除时,delete
只返回false。
要理解这一点,我们首先需要掌握像变量实例化和属性特性这样的概念--遗憾的是这些在关于javascript的书中很少讲到。我将在接下来的几个段落中试着简明的重温这些概念。 理解它们一点也不难,如果你不在乎它们为什么这么运行,你可以随意的跳过这一章。
代码类型
在ECMAScript中有三种类型的可执行代码:全局代码(Global code)、函数代码(Function code)和Eval code。这些类型有那么点自我描述,但这里还是作一个简短的概述:
- 当一段源代码正文被视为程序时,它在全局作用域中执行,被当成全局代码(Global code)。在一个浏览器环境中,SCRIPT元素中的内容通常被当作程序来解析,因此,它被当作全局代码来评估。
- 在一个函数内部直接执行的任何代码,很明显被当作函数代码(Function code)。在浏览器红中事件属性的内容(如:
<p onclick="...">
)通常被当作函数代码(Function code)来解析; - 最后,提供给内置函数eval()的文本被当作Eval 代码(Eval code)来解析。我们很快会看到这种类型很特殊。
执行上下文
当ECMAScript 代码执行时,它总是在一定的上下文中运行,执行上下文是一个有点抽象的实体,它有助于我们理解作用域和变量实例化如何工作的。对于三种类型的可执行代码,每个都有执行的上下文。当一个函数执行时,可以说控制进入到函数代码(Function code)的执行上下文。全局代码执行时,进入到全局代码(Global code)的执行上下文。
正如你所见,执行上下文逻辑上来自一个栈。首先可能是有自己作用域的全局代码,代码中可能调用一个函数,它有自己的作用域,函数可以调用另外一个函数,等等。即使函数递归地调用它自身,每一次调用都进入一个新的执行上下文。
激活对象/可变对象
每一个执行上下文在其内部都有一个所谓的可变对象。与执行上下文类似,可变对象是一个抽象的实体,一个描述变量示例化的机制。现在,最有趣的是在源代码中声明的变量和函数被当作这个可变对象的属性被添加。
当控制进入全局代码的执行上下文时,一个全局对象用作可变对象。这也正是为什么在全局范围中声明的变量或者函数变成了全局对象的属性。
2 var GLOBAL_OBJECT = this;
3 var foo = 1;
4 GLOBAL_OBJECT.foo; // 1
5 foo === GLOBAL_OBJECT.foo; // true
6 function bar(){}
7 typeof GLOBAL_OBJECT.bar; // "function"
8 GLOBAL_OBJECT.bar === bar; // true
Ok,全局变量变成了全局对象的属性,但是,那些在函数代码(Function code)中定义的局部变量又会如何呢?行为其实很相似:它成了可变对象的属性。唯一的差别在于在函数代码(Function code)中,可变对象不是全局对象,而是所谓的激活对象。每次函数代码(Function code)进入执行作用域时,激活对象即被创建。
不仅函数代码(Function code)中的变量和函数成为激活对象的属性,而且函数的每一个参数(与形参相对应的名称)和一个特定Arguments
对象(Arguments
)也是。注意,激活对象是一种内部机制,不会被程序代码真正访问到。
2 var bar = 2;
3 function baz(){}
4 /*
5 In abstract terms,
6 Special `arguments` object becomes a property of containing function's Activation object:
7 ACTIVATION_OBJECT.arguments; // Arguments object
8 ...as well as argument `foo`:
9 ACTIVATION_OBJECT.foo; // 1
10 ...as well as variable `bar`:
11 ACTIVATION_OBJECT.bar; // 2
12 ...as well as function declared locally:
13 typeof ACTIVATION_OBJECT.baz; // "function"
14 */
15 })(1);
最后,在Eval 代码(Eval code)中声明的变量作为正在调用的上下文的可变对象的属性被创建。Eval 代码(Eval code)只使用它正在被调用的哪个执行上下文的可变对象。
2 /* `foo` is created as a property of calling context Variable object,
3 which in this case is a Global object */
4 eval('var foo = 1;');
5 GLOBAL_OBJECT.foo; // 1
6 (function(){
7 /* `bar` is created as a property of calling context Variable object,
8 which in this case is an Activation object of containing function */
9 eval('var bar = 1;');
10 /*
11 In abstract terms,
12 ACTIVATION_OBJECT.bar; // 1
13 */
14 })();
未完待续...