有人可以在JS中提供变量范围的解释,因为它适用于对象,函数和闭包吗?
答案 0 :(得分:35)
Javascript中的每个变量都是对象的命名属性。例如: -
var x = 1;
x被添加到全局对象中。全局对象由脚本上下文提供,并且可能已经具有一组属性。例如,在浏览器中,全局对象是窗口。与浏览器中的上述行等价的是: -
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
替换为您上下文中对stdout的任何写入。
你应该得到的顺序是: -
1 1 2 2 3 4 3 4
那么这里发生了什么?我们有fnSequence
将变量x
初始化为1并返回一个匿名函数,该函数将返回x
的值,然后递增它。
首次执行此函数时,将创建一个范围对象,并将属性x
添加到该范围对象,其值为1.还在同一执行对象中创建的是匿名函数。每个函数对象都有一个scope属性,该属性指向创建它的执行上下文。这会创建一个我们将在稍后介绍的范围链。 fnSequence
返回对此函数的引用,并存储在fn1
。
请注意,fn1
现在指向匿名函数,并且匿名函数的范围属性指向仍附加x
属性的范围对象。这称为closure
,其中执行上下文的内容在为其创建的函数完成执行后仍可访问。
现在,在分配给fn2
时会发生同样的序列。 fn2
将指向一个不同的匿名函数,该函数是在第二次调用fnSequence
时创建的不同执行上下文中创建的。
第一次执行fn1
所持有的功能时会发生什么?为执行匿名函数创建新的执行上下文。可以从标识符x
中找到返回值。检查函数的范围对象是否为x
属性,但未找到任何属性。这就是范围链的用武之地。在当前执行上下文中未能找到x
JavaScript获取函数的scope属性所持有的对象并在那里查找x
。它找到它,因为函数范围是在fnSequence
的执行中创建的,检索它的值并递增它。因此输出1并且此范围中的x
增加到2.
现在执行fn2
时,它最终附加到x
属性仍为1的不同执行上下文。因此执行fn2
也会导致1。
正如您所看到的,fn1
和fn2
每个都会生成自己独立的数字序列。
答案 1 :(得分:4)
未使用var声明的变量在范围内是全局变量。 函数引入了作用域,但请注意,如果块和其他块不引入作用域。
我还可以通过Google搜索Javascript范围看到有关此内容的更多信息。这真的是我推荐的。 http://www.digital-web.com/articles/scope_in_javascript/
答案 2 :(得分:1)
函数引入范围。您可以在其他函数内声明函数,从而创建嵌套作用域。内部作用域可以访问外部作用域,但外部作用域不能访问内部作用域。
使用 var 关键字将变量绑定到范围。所有变量都隐式绑定到顶级范围。因此,如果省略var关键字,则隐式引用绑定到顶级的变量。在浏览器中,顶级是窗口对象。请注意,窗口是自变量,因此窗口 == window.window