这个也不会,回家种田去吧你
顺便我们来谈谈delete的用法
几个礼拜前, 我有了个机会去翻阅Stoyan Stefanov的 Object-Oriented Javascript 一书. 这本书在亚马逊上拥有很高的评价(12篇评论, 5颗星), 所以我很好奇地想看看它到底是不是那么值得推荐的一本书, 于是我开始阅读函数的那章. 我非常欣赏这本书解释事物的方式, 例子们被以一种非常漂亮, 渐进的方式被组织起来, 看起来即便是初学者也能够轻松掌握这些知识. 然而, 几乎是立刻, 我就发现了一个贯穿整个章节的有趣的误解——删除功能函数. 另外还有一些其它错误(例如函数声明与函数表达式的区别), 但是我们目前将不去讨论它们.
这本书声称:
\"函数被作为像一般变量一样对待-它可以被复制到不同的变量中, 甚至被删除\". 在这个解释后面附加了这样一段示例:
忽略掉一些漏掉的分号, 你能看出这几句代码的错误在哪么? 显然, 错误在于删除sum这个变量的操作是不会成功的. delete表达式不应该返回true, 并且 typeof sum也不应该返回\"undefined\". 这一切都因为在JavaScript中删除变量是不可能的. 至少, 在这种声明方式下是不可能的.
所以, 在这个例子中到底发生了什么? 它是一个错误么? 抑或是一个特殊用法? 大概不是这样的. 这一段代码事实上是Firebug控制台中的真实输出, Stoyan一定是使用了它作为快速测试的工具. 这几乎就好像是Firebug遵守了其它一些delete的规则一样. 是Firebug导致了Stoyan误入歧途! 所以, 这儿到底发生了什么?
在回答这个问题之前, 我们首先需要理解delete运算符到底在JavaScript中是如何工作的: 到底什么能够被删除, 什么不能够被删除? 今天, 我将尝试着详细解释这个问题. 我们将看看Firebug的\"奇怪\"行为并且意识到它其实并不是那么奇怪. 我们将深入了解在声明变量, 函数, 给属性赋值和删除它们的这些场景背后到底隐藏了什么. 我们将看看浏览器的兼容性和一些最臭名昭著的bug. 我们还将讨论ES5的严格模式, 和它如何改变delete操作符的行为.
我将交换着使用JavaScript和ECMAScript, 它们都意味着ECMAScript(除非明显地谈论Mozilla的JavaScript实现)
不出所料, 在网络上, 对delete的解释是相当稀缺的. MDC article大概是最好理解的资源了, 但是, 不幸的是, 它缺失了这个主题的一些有趣的细节. 奇怪的是, 其中一个被遗忘的东西就是Firebug的奇怪表现的原因. 而MSDN reference在这些方面几乎是无用处的.
Theory
那么, 为什么我们能够删除对象的属性:
却不能删除这样声明的对象:
或者函数呢:
注意: 当一个属性无法被删除时,delete操作符只会返回false
要理解这个, 我们首先需要掌握这些有关变量实例和属性特性的概念——这些概念很不幸地, 很少在JavaScript书中被提及. 我将试着在接下来的几个段落中简单地复习一下这些概念. 这些概念是很难理解的!如果你不在乎\"为什么这些东西会以这种方式工作\"的话,尽情跳过这一章节好了.
代码的类型:
在ECMAScript中, 有3种不同类型的可执行代码: 全局代码(Global code), 函数代码(Function code)和 Eval代码(Eval code). 这些类型从名称上来说或多或少是有自解释性的, 这里有一个简短的概述:
当一段源代码被看成程序(Program)时, 它将会在全局环境下被执行, 并且被认为是全局代码(Global code). 在一个浏览器环境中, 脚本元素的内容通常被解释为程序, 因此被作为全局代码来执行.
任何直接在一个函数中执行的代码显然被认为是函数代码(Function code). 在浏览器中, 事件属性的内容(如 <p onclick=\"....\">)通常被解释成函数代码.
最后, 被应用到内置函数eval的代码文本被解释成Eval代码(Eval code). 很快我们会发现为什么这种类型是特殊的.
执行上下文(Execution context):
当ECMAScript代码执行时, 它通常会发生在特定的执行上下文中.执行上下文是一个有些抽象的实体概念, 它能帮助理解范围(Scope)和变量实例(Variable instantiation)是如何工作的. 对三种可执行代码的每一种, 都有一个执行上下文相对应. 当一个函数被执行的时候, 我们说\"程序控制进入了函数代码的执行上下文\"; 当一段全局代码被执行时, 程序控制进入了全局代码的执行上下文, 等等.
正如你所见, 执行上下文可以在逻辑上构成一个堆栈. 首先, 可能有一段全局代码和其自己的执行上下文, 然后这段代码可能会调用一个函数, 并带着它(函数)的执行上下文. 这段函数可以调用另外一个函数, 等等等等. 即使函数是递归调用的, 每次调用时被也会进入一个新的执行上下文.
活动对象(Activation object) / 变量对象(Variable Object):
每一个执行上下文都有一个跟其所关联的所谓变量对象(Variable Object). 类似于执行上下文, 变量对象是一个抽象实体, 一种用来描述变量实例的机制. 有趣之处在于, 在源代码中声明的变量和函数通常会被当做属性(properties)增加到这个变量对象上.
当程序控制进入全局代码的执行上下文时, 一个全局对象(Global object)被用来作为一个变量对象. 这正是为什么声明为全局的函数变量会变成全局对象属性的原因.
var foo = 1;
GLOBAL_OBJECT.foo; // 1
foo === GLOBAL_OBJECT.foo; // true
function bar(){}
typeof GLOBAL_OBJECT.bar; // \"function\"
GLOBAL_OBJECT.bar === bar; // true
本文地址:https://www.stayed.cn/item/1262
转载请注明出处。
本站部分内容来源于网络,如侵犯到您的权益,请 联系我