2011-05-13 50 views
3

比较这段代码删除:有人可以解释这种行为

somevar = 5; 
    delete window.somevar; 
    alert(typeof somevar) //=> undefined, so deleted 

这段代码:第一块

var somevar = 5; 
    delete window.somevar; 
    alert(typeof somevar) //=> number, so NOT deleted 

See it in action here

现在,somevar是删除,在第二个块它不是。唯一的区别是在第二个块中使用var关键字。这两个块都在全局范围内运行。

这可以解释吗?

代码不能在铬控制台或萤火中的jsfiddle任一测试,并且不。在这些环境中,所有代码都是evalled,并且在代码delete中的任何代码均适用于eval(请参阅more about that)的结果。无论如何,在IE 是不允许的。

回答

10

你所看到的事实是全局对象(window,在浏览器上)是两个不同事物的混合,除了全局执行上下文外,它们是独特的。

在第一块,someVarwindow对象的正常特性。属性可以通过delete删除。

在第二块中,someVar结合对象全局执行上下文  —这也是window可变上下文的性质。您无法删除绑定对象在其作为绑定对象的角色中接收的属性(即使您可以删除以其他方式接收的属性)。也就是说,您不能删除使用var声明的变量(以及其他一些以相同方式添加的内容)。

(对不起,不是我的术语,它来自the spec,这的确具有一些非常有趣的语言。)

这只是我们有这个概念混为一谈全球执行上下文。其他执行上下文(例如函数调用)的变量绑定对象仍然是一件非常真实的事情(并且对于闭包的正常运行至关重要),但是没有直接访问它的程序化方法。然而,在全局执行上下文中,它是全局对象,我们当然可以访问它。

如果我们先看看函数,然后再看看全局执行上下文,它有助于理解这一点。当你调用一个函数,这些事情发生:

  1. 设置this指向通过调用指定的对象(的this值通常是隐式设置,但有办法来设置它明确)。
  2. 为此调用创建一个执行上下文
  3. 创建该执行上下文变量上下文
  4. 该变量上下文创建结合对象
  5. 将函数的名称(如果有)添加到绑定对象作为引用该函数的属性。
  6. arguments属性添加到绑定对象,参照参数的伪阵列的功能。
  7. 将函数定义中声明的任何命名参数添加为绑定对象的属性,并引用它们在参数中的条目。
  8. 与价值undefined添加的通过var报表(任何地方函数体)作为绑定对象的属性声明的任何变量的名称,最初。
  9. 如果在函数中声明了命名函数,请将它们的名称作为绑定对象的属性添加,并引用这些函数。
  10. 把绑定对象在范围链(详见下文)的顶部。

...然后逐步执行函数体内的代码开始。任何var语句带有初始值(例如,var a = 5;而不仅仅是var a;被视为赋值语句(a = 5;)当执行点到达他们。

纵观上述,只要属性添加“到绑定对象”,它的加入一个标志,表明它不能被删除。这就是为什么var S(和声明的函数等的名称)不能被删除。

任何不合格的参考通过作用域链抬头。所以,当您在代码中引用a时,解释器看起来的第一个位置是位于顶部的绑定对象范围链。如果它有一个名为a的属性,那就是被使用的;如果不是,我们查看范围链中的下一个链接,如果我们找到它,则使用该属性;依此类推,直到我们用尽范围链上的链接。全局对象是该链最底层的链接(这就是全局变量工作的原因)。

那么全球环境有什么不同呢?其实,很少。下面是序列(大致):

  1. 为此调用创建一个执行上下文
  2. 创建该执行上下文变量上下文
  3. 为该变量上下文创建一个绑定对象
  4. 设置this指向绑定对象;这使其成为全球性的对象。
  5. 根据环境的定义在该对象上设置一些默认属性(在浏览器中,例如,将属性window添加到对象中,引用自身)。

...然后我们基本上拿起第8步中的函数的东西:

  • 添加的通过var报表(任何地方在全球范围内)作为绑定/全局对象的属性声明的任何变量的名称,最初值为undefined
  • 如果在全局范围内声明了命名函数,请将它们的名称作为绑定/全局对象的属性添加,并引用这些函数。
  • 将绑定/全局对象置于范围链(更多下方)的顶部。

...并开始逐步执​​行代码(同样使用var初始值设定项成为赋值)。

+0

谢谢TJ。随着你的回答,结合ECMA规范,我开始明白,为什么'eval'-ed代码变量可以被删除。假设这触及了它:*如果一个VariableDeclaration嵌套在with语句中,并且VariableDeclaration中的标识符是与with语句的对象环境记录的绑定对象的属性名称相同的 ,则步骤4 将赋值给属性,而不是标识符的变量环境绑定。* – KooiInc 2011-05-13 09:14:49

+0

@KooiInc:LOL,这里我避免谈论'with'语句。顺便说一下,我在上面添加了一些进一步的解释。 – 2011-05-13 09:19:08

+0

确实很有趣。也许我们应该把它归结为:*如果你不使用var,我们隐含地假设你的意思是:'with(this){somevar = [assignment];}',相当于'this.somevar = [赋值] '*?无论如何,让我们先接受你的回答;) – KooiInc 2011-05-13 09:34:01

相关问题