【发布时间】:2010-09-11 03:00:36
【问题描述】:
谁能解释一下 JS 中的变量作用域,因为它适用于对象、函数和闭包?
【问题讨论】:
标签: javascript oop
谁能解释一下 JS 中的变量作用域,因为它适用于对象、函数和闭包?
【问题讨论】:
标签: javascript oop
未使用 var 声明的变量在范围内是全局的。 函数引入了作用域,但注意 if 块和其他块不引入作用域。
我还可以通过谷歌搜索 Javascript 范围看到很多关于此的信息。这真的是我推荐的。 http://www.digital-web.com/articles/scope_in_javascript/
【讨论】:
Javascript 中的每个变量都是对象的命名属性。例如:-
var x = 1;
x 被添加到全局对象中。全局对象由脚本上下文提供,并且可能已经具有一组属性。例如,在浏览器中,全局对象是 window。与浏览器中的上述行等效的是:-
window.x = 1;
现在如果我们把它改成:-
function fn()
{
var x = 1;
}
当fn 被调用时,会创建一个名为执行上下文 的新对象,也称为作用域(我可以互换使用这些术语)。 x 作为属性添加到此范围对象。因此,对fn 的每次调用都将获得它自己的范围对象实例,因此它自己的 x 属性实例附加到该范围对象。
现在让我们更进一步:-
function fnSequence()
{
var x = 1;
return function() { return x++; }
}
var fn1 = fnSequence();
var fn2 = fnSequence();
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn1())
WScript.Echo(fn1())
WScript.Echo(fn2())
WScript.Echo(fn2())
注意:将 WScript.Echo 替换为您的上下文中写入标准输出的任何内容。
你应该得到的序列是:-
1 1 2 2 3 4 3 4
那么这里发生了什么?我们有fnSequence,它将变量x 初始化为1,并返回一个匿名函数,该函数将返回x 的值,然后将其递增。
当这个函数第一次被执行时,一个作用域对象被创建并且一个属性x被添加到那个值为1的作用域对象中。同样在同一个执行对象中创建了一个匿名函数。每个函数对象都有一个作用域属性,它指向创建它的执行上下文。这创建了一个我们将在后面讨论的作用域链。对此函数的引用由fnSequence 返回并存储在fn1 中。
请注意,fn1 现在指向匿名函数,并且匿名函数有一个作用域属性指向一个作用域对象,该对象仍然附加了一个x 属性。这被称为closure,其中执行上下文的内容在为其创建的函数完成执行后仍然可以访问。
现在分配给fn2 时会发生同样的顺序。 fn2 将指向另一个匿名函数,该函数是在第二次调用 fnSequence 时创建的不同执行上下文中创建的。
fn1 持有的函数第一次执行时会发生什么?为执行匿名函数创建一个新的执行上下文。可以从标识符x 中找到返回值。检查函数的作用域对象是否有x 属性,但没有找到。这就是 作用域链 的用武之地。在当前执行上下文中找不到 x 时,JavaScript 会获取函数的作用域属性持有的对象并在那里查找 x。因为函数作用域是在fnSequence 的执行中创建的,所以它找到了它,检索它的值并递增它。因此输出 1 并且此范围内的 x 递增到 2。
现在,当fn2 执行时,它最终会附加到另一个执行上下文,其x 属性仍为1。因此执行fn2 也会导致1。
如您所见,fn1 和 fn2 各自生成自己独立的数字序列。
【讨论】:
var x = 1; 和 window.x = 1; 不等效。在第二种情况下,x 是可删除的。首先,它不是。使用术语“属性”而不是“变量”或“属性”是没有帮助的,因为“属性”对于 JavaScript 开发人员来说已经有两种含义,而这两种含义都不是您正在使用的含义。此外,您对执行上下文的描述是错误的:如果您要使用 ECMAScript 规范中的术语,那么您确实需要准确地使用它们。 [继续]
fn被调用时,x被创建为变量对象的一个属性,它绑定到执行上下文但和它不是一回事.将术语“范围”与“执行上下文”互换使用也无济于事:同样,它们密切相关,每个执行上下文都有一个范围链,但它们不是一回事。例如,可以使用with 语句或catch 子句由执行上下文中的代码更改作用域链。
函数引入范围。您可以在其他函数中声明函数,从而创建嵌套范围。内部作用域可以访问外部作用域,而外部作用域不能访问内部作用域。
变量被绑定到一个范围,使用 var 关键字。所有变量都隐式绑定到顶级范围。因此,如果您省略 var 关键字,您将隐式引用绑定到顶层的变量。在浏览器中,顶层是 window 对象。请注意,window 是自变量,所以 window == window.window
【讨论】: