深入理解JS的delete
delete 运算符 从对象中删除一个属性,或从数组中删除一个元素。
1. 基本原理
我们可以删除对象的某个属性:
var o = { x: 1 }; delete o.x; // true o.x; // undefined
但不能删除变量,比如以下面这种方式声明的:
var x = 1; delete x; // false x; // 1
也不能删除函数,比如下面所定义的:
function x(){} delete x; // false typeof x; // "function"
注意 如果某个属性不能被删除的话,delete操作会返回false.
1.1 可执行代码的分类
在 ECMAScript 中有3种类型的可执行代码: 全局代码, 函数代码, 以及 Eval 代码.
这些类型是自描述的,下面是一个简短的概述:
当一个文本 source 被当做作一段程序,它在全局范围内执行,被认为是全局代码(Global code).在浏览器环境中, SCRIPT 元素的内容通常被解析为程序,因此等价于全局代码.直接在函数内执行的东西,很明显,被认为是一段函数代码(Function code).在浏览器中,事件属性的内容(例如
)通常被解析并被认为是一段函数代码.最后,在内置 eval 函数中的文本被解析为 Eval code. 我们很快就会看到为什么这种类型是特殊的.
1.2 执行上下文
当 ECMAScript 代码执行时,它总是处于特定的执行上下文中的.执行上下文是一个抽象的存在,这有助于理解 scope 和 变量实例 是如何工作的的. 对于三种类型的可执行代码,每种都有一个执行上下文.当一个函数执行时,可以说被控制着进入 Function代码执行上下文;当全局代码执行时,进入全局代码的执行上下文 等等.
正如您所见到的,执行上下文在逻辑上形成一个堆栈.首先是全局代码及其执行上下文;而全局代码可以调用一个函数,有函数自己的执行上下文,该函数可以调用另一个函数,等等等等.即使函数递归地调用其本身,每一次调用也会进入一个新的执行上下文.
1.3 Activation对象/Variable对象
每个执行上下文都有一个被叫做 Variable object (活化对象?)的对象与其相关联.类似于执行上下文,Variable 对象也是一个抽象的存在,用来描述变量实例化的一种机制.现在,有意思的是,在一个源文本中声明的变量和函数中实际上都被添加为该 Variable object 对象的属性(properties).
当进入全局代码执行上下文,全局对象(Global object,如浏览器中的 window)被当做其 Variable object 对象.这正是为什么在全局范围内声明的变量或函数会成为全局对象的属性的原因:
/* 当在全局范围时, `this` 指向的就是 global object */ 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 对象的属性(properties).唯一的区别是,当在函数代码中时,Variable 对象并不是全局对象,而是一个称为Activation object 的对象.每次进入函数的执行上下文时都会创建一个 Activation object 对象.
不仅函数内部声明的变量和函数会成为 Activation object 对象的属性;而且函数的每个参数(对应到相应的参数名)、以及一个特殊的 Arguments 对象(名为 arguments )也会成为 Activation object 对象的属性.注意, Activation 对象只是一种内部的机制,程序永远不可能真正地访问(引用)到这个对象.
(function(foo){ var bar = 2; function baz(){} /* 以抽象虚拟的说法, 专有的 `arguments` 对象成为对应函数 Activation 对象的一个属性(property): ACTIVATION_OBJECT.arguments; // Arguments 对象 ...当然,参数 `foo`也是一样的道理: ACTIVATION_OBJECT.foo; // 1 ...同时,局部变量 `bar`同样如此: ACTIVATION_OBJECT.bar; // 2 ...定义的局部函数也是如此: typeof ACTIVATION_OBJECT.baz; // "function" */ })(1);
最后,在 Eval 代码内声明的变量被创建为调用上下文 Variable object 对象的属性.简言之,Eval代码在哪里被调用,内部的变量就相当于在哪里被声明:
/* 此处eval内的`foo` 被创建为 调用上下文 Variable 对象的属性, 在此处上下文对象即为全局对象 Global object */ eval('var foo = 1;'); GLOBAL_OBJECT.foo; // 1 (function(){ /* `bar` 被创建为 调用上下文 Variable object 对象的一个属性, 此处 Variable object 对象是包含的function的一个 Activation 对象 */ eval('var bar = 1;'); /* 以一种虚拟抽象的角度看, ACTIVATION_OBJECT.bar; // 1 */ })();