好, 所以全局变量会变成全局对象的属性, 但是局部变量(那些在函数代码中定义的变量)会发生什么呢? 其实它们的行为也非常类似: 它们会变成变量对象(Variable object)的属性. 唯一的不同在于, 当在函数代码中时, 一个变量对象并不是全局对象, 而是所谓的活动对象(Activation object). 活动对象在会每次进入函数代码的执行上下文时被创建. 并不是只有在函数代码中声明的变量和函数会变成活动对象的属性; 这也会在每个函数参数(对应相应的形式参数的名称)和一个特殊的Arguments对象(以arguments为名称)上发生. 注意, 活动对象是一个内部描述机制, 在程序代码中并不能被访问. 复制代码 代码如下: (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代码中声明的变量会成为调用者上下文(calling context)的变量对象的属性. Eval代码只是简单地使用调用它的代码的执行上下文的变量对象. 复制代码 代码如下: var GLOBAL_OBJECT = this; /* `foo` is created as a property of calling context Variable object, which in this case is a Global object */ eval("var foo = 1;"); GLOBAL_OBJECT.foo; // 1 (function(){ /* `bar` is created as a property of calling context Variable object, which in this case is an Activation object of containing function */ eval("var bar = 1;"); /* In abstract terms, ACTIVATION_OBJECT.bar; // 1 */ })();
属性的特性(property attributes) 我们几乎是已经在这了. 既然我们已经很清楚在变量上发生了什么(它们变成了属性), 唯一剩下的需要理解的概念就是属性的特性(property attributes)了. 每一个属性可以拥有0个或多个特性, 它们从以下集合中选取: ReadOnly, DontEnum, DontDelete和 Internal. 你可以把它们认为是flags —— 一种特性可以在属性中存在, 也可以不存在. 对于我们今天的讨论来说, 我们只对DontDelete感兴趣. 当被声明的变量和函数成为变量对象(或者函数代码的活动对象, 或全局代码的全局对象)的属性时, 这些属性在创建时就带上了DontDelete的特性. 然而, 任何显式(或隐式)的属性赋值所建立的属性将不会被带上DontDelete特性. 这就是为什么我们能够删除一些属性, 但删除不了其它的. 复制代码 代码如下: var GLOBAL_OBJECT = this; /* `foo` is a property of a Global object. It is created via variable declaration and so has DontDelete attribute. This is why it can not be deleted. */ var foo = 1; delete foo; // false typeof foo; // "number" /* `bar` is a property of a Global object. It is created via function declaration and so has DontDelete attribute. This is why it can not be deleted either. */ function bar(){} delete bar; // false typeof bar; // "function" /* `baz` is also a property of a Global object. However, it is created via property assignment and so has no DontDelete attribute. This is why it can be deleted. */ GLOBAL_OBJECT.baz = "blah"; delete GLOBAL_OBJECT.baz; // true typeof GLOBAL_OBJECT.baz; // "undefined"
内置对象和DontDelete 所以, 这就是有关它(DontDelete)的所有: 属性的一个特殊特性, 用来控制这个属性是否能够被删除. 注意, 有些内置对象的属性是指定含有DontDelete的, 所以无法被删除. 如特殊的arguments变量(或者, 正如我们现在所知道的, 一个活动对象的属性)拥有DontDelete. 函数实例的length属性也具有DontDelete属性. 复制代码 代码如下: (function(){ /* can"t delete `arguments`, since it has DontDelete */ delete arguments; // false typeof arguments; // "object" /* can"t delete function"s `length`; it also has DontDelete */ function f(){} delete f.length; // false typeof f.length; // "number" })();
未声明的赋值: 你可能还记着, 未声明的赋值会在全局对象上建立一个属性, 除非这个属性已经在这个作用域链中全局对象之前的其它地方被找到. 并且, 现在我们知道属性赋值和变量声明的不同之处——后者会设置DontDelete属性, 但前者不会. 我们必须清楚, 为什么未声明的赋值会建立一个可删除的属性. 复制代码 代码如下: var GLOBAL_OBJECT = this; /* create global property via variable declaration; property has DontDelete */ var foo = 1; /* create global property via undeclared assignment; property has no DontDelete */ bar = 2; delete foo; // false typeof foo; // "number" delete bar; // true typeof bar; // "undefined"
请注意: 特性是在属性被创建时被决定的, 之后的赋值不会修改已存在属性的特性. 理解这一点区别非常重要. 复制代码 代码如下: /* `foo` is created as a property with DontDelete */ function foo(){} /* Later assignments do not modify attributes. DontDelete is still there! */ foo = 1; delete foo; // false typeof foo; // "number" /* But assigning to a property that doesn"t exist, creates that property with empty attributes (and so without DontDelete) */ this.bar = 1; delete bar; // true typeof bar; // "undefined"