19

有人可以解释 JS 中的变量范围,因为它适用于对象、函数和闭包吗?

4

3 回答 3

35

全局变量

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执行上下文的内容在为其创建的函数完成执行后仍然可以访问的地方。

现在,分配给 时会发生相同的序列fn2fn2将指向另一个匿名函数,该函数是在fnSequence第二次调用时创建的不同执行上下文中创建的。

范围链

持有的函数fn1第一次执行时会发生什么?为执行匿名函数创建一个新的执行上下文。从标识符中可以找到返回值x。检查函数的作用域对象的x属性,但没有找到。这就是作用域链出现的地方。在当前执行上下文中找不到xJavaScript 会获取函数的作用域属性持有的对象并在x那里查找。它找到它,因为函数作用域是在 的执行中创建的fnSequence,检索它的值并递增它。因此输出 1,并且x在这个范围内的 增加为 2。

现在当fn2被执行时,它最终会附加到一个不同的执行上下文,其x属性仍然是 1。因此执行fn2也会导致 1。

如您所见fn1fn2每个都生成自己独立的数字序列。

于 2008-09-19T10:57:50.103 回答
4

未使用 var 声明的变量在范围内是全局的。函数引入范围,但请注意 if 块和其他块不引入范围。

通过谷歌搜索 Javascript 范围,我还可以看到很多关于此的信息。这真的是我推荐的。 http://www.digital-web.com/articles/scope_in_javascript/

于 2008-09-19T05:49:17.347 回答
1

函数引入范围。您可以在其他函数中声明函数,从而创建嵌套范围。内部范围可以访问外部范围,但外部不能访问内部范围。

Variables are bound to a scope, using the var keyword. All variables are implicitly bound to the top-level scope. So if you omit the var keyword, you are implicitly referring to a variable bound to the top level. In a browser, the top level is the window object. Note that window is it self a variable, so window == window.window

于 2008-09-19T11:11:31.223 回答