标签:
在这篇文章中作者从《JavaScript面向对象编程指南》一书中关于 delete 的错误讲起,详细讲述了关于 delete 操作的实现, 局限以及在不同浏览器和插件(这里指 firebug)中的表现。
下面翻译其中的主要部分。
...书中声称
“函数就像一个普通的变量那样——可以拷贝到不同变量,甚至被删除”
并附上了下面的代码片段作为说明:
var sum = function(a, b) {return a + b;}; var add = sum; delete sum; true // 测试,在Chrome里显示false typeof sum; "undefined"
你能发现片段中的问题吗? 这个问题就是——删除 sum 变量的操作不应该成功; delete 的声明不应该返回 true 而 typeof sum 也不应该返回为 undefined。 因为,javascript 中不能够删除变量,至少不能以这个方式声明删除。
那么这个例子发生了什么? 是打印错误或者玩笑? 应该不是。 这个片段是 firebug 控制台中的一个实际输出,而 Stoyan(上面所说书的作者)应该正是用它做的快速测试。 这仿佛说明了 firebug 有一些不同的删除规则。 正是 firebug 误导了 Stoyan! 那么这里面究竟是怎么回事呢?
为了回答这个问题,我们需要了解 delete 运算符在 Javascript 中是如何工作的: 哪些可以被删除,哪些不能删除以及为什么。 下面我试着解释一下这方面的细节。 我们将通过观察 firebug 的“奇怪”的表现而认识到它实际上完全不“奇怪”; 我们将深入了解那些,当我们声明变量、函数,赋值属性和删除它们时的,隐藏在背后的细节; 我们将看一下浏览器对此的实现和一些有名的 bug; 我们还会讨论到 ECMAScript 版本 5 中的严格模式(strict mode)以及它如何改变 delete 运算符的行为。
我在下面交替使用的 Javascript 和 ECMPScript 一般都指 ECMAScript(除非当明确谈到 Mozilla 的 JavaScript™ 实现时)。
意料之中的,网络上目前对于 delete 的解释非常少(笔者按:这篇文章写于 2010 年 1 月)。 MDC(MDN]) 的资源大概是这其中最详细的了,但不幸的是它遗漏了一些有趣的细节,这些细节中就包括了上述 firebug 的奇怪表现。 MSDN 文档几乎没什么用处。
那么,为什么我们能删除一个对象的属性:
var x = { a: 1 }; delete x.a; // true x.a; // undefined
但却不能删除一个变量:
var x = 1; delete x; // false; x; // 1
也不能删除一个函数:
function x() {}; delete x; // false;
typeof x; // "function"
注意:delete 只有当一个属性无法被删除时才返回 false。
为了理解这一点,我们需要首先把握一些概念: 变量实例化(variable instantiation)和属性的内部属性(property attributes) (译者按:关于 property 和 attributes 的区别见参考文章,根据下面涉及到的内容,拟译成内部属性) ——这些很少在 javascript 书中被提到。 在下面几段中我将试着简短地回顾这些内容,要理解它们并不难。 如果你并不关注它们表现背后的原因,可以跳过这一章。
ECMAScript 中有三类可执行代码:
这几类的含义大致就像它们命名的那样,但还是快速地回顾一下:
当一个源文件被看做是一个程序,它在全局作用域(scope)内执行,而这就被认为是一段全局代码 Global code。 在浏览器环境下,SCRIPT 元素的内容通常都被解析为一个程序,因而作为全局代码来执行。
当然,任何在一段函数中直接执行的代码就被认为是一段函数代码 Function code, 在浏览器环境下,事件属性的内容(e.g. <a onclick="..."
)通常都作为函数代码来解析和执行。
最后,放入内建函数 eval
中的代码就作为 Eval code 来解析。我们将很快看到为什么这一类型是特殊的。
当 ECMAScript 代码执行时,它总是发生在一个确定的执行上下文(context)中。 执行作用域是一个抽象实体,它有助于理解作用域和变量实例化的工作原理。 上面三类可执行代码都有各自的执行上下文。 当函数代码执行时,我们说控制端进入了函数代码的执行上下文; 当全局代码执行时,我们说控制端进入了全局代码的执行上下文,以此类推。
正如你所见,执行上下文在逻辑上是一个栈(stack)。 首先可能有一段全局代码,它拥有属于自己的执行上下文; 在这段代码中可能调用一个函数,这个函数同样拥有属于自己的执行上下文; 这个函数可能调用另一个函数,等等。 即使当函数递归调用自己时,在每一步调用中仍然进入了不同的执行上下文。
每一个执行上下文都有一个与之相关联的变量对象(Variable object)。 和它相似的,变量对象也是一个抽象实体,一种用来描述变量实例化的机制。 而有趣的是,在一段源代码中声明的变量和函数事实上被作为变量对象(Variable object)的属性(properties)而添加到变量对象中。
当控制进入了全局代码的执行上下文时,一个全局对象被用作变量对象。 这恰恰是为什么全局声明的变量和函数变成一个全局对象的属性的原因:
标签:
原文地址:http://www.cnblogs.com/abapscript/p/4930397.html