在Javascript中,函数启动一个新的作用域,但我们必须小心必须调用该函数以便创建作用域,是这样的吗?

时间:2010-04-30 06:25:46

标签: javascript scope closures

在Javascript中,我有时过于沉浸在函数创建新范围的想法中,有时我甚至认为以下匿名函数在定义并分配给onclick时会创建一个新范围:

<a href="#" id="link1">ha link 1</a>
<a href="#" id="link2">ha link 2</a>
<a href="#" id="link3">ha link 3</a>
<a href="#" id="link4">ha link 4</a>
<a href="#" id="link5">ha link 5</a>


<script type="text/javascript">

    for (i = 1; i <= 5; i++) {

        document.getElementById('link' + i).onclick = function() { var x = i; alert(x); return false; }
    }

</script>

但事实上,匿名函数会创建一个新的范围,这是正确的,但只有在调用它时才是这样吗?因此,不会创建匿名函数内的x,也不会创建新范围。稍后调用该函数时,会有一个新的范围,但i在外部范围内,而x获取其值,并且它总是为6。

下面的代码实际上会调用一个函数并创建一个新的范围,这就是为什么x每次都是全新范围内的新局部变量x,以及函数的调用时单击该链接将使用不同范围中的不同x

<a href="#" id="link1">ha link 1</a>
<a href="#" id="link2">ha link 2</a>
<a href="#" id="link3">ha link 3</a>
<a href="#" id="link4">ha link 4</a>
<a href="#" id="link5">ha link 5</a>


<script type="text/javascript">

    for (i = 1; i <= 5; i++) {

        (function() {
            var x = i;
            document.getElementById('link' + i).onclick = function() { alert(x); return false; }
        })();  // invoking it now!
    }

</script>

如果我们删除了var前面的x,那么它就是全局x,因此在新范围内没有创建局部变量x,因此,点击链接会得到所有相同的数字,这是全局x的值。

更新:问题是:我们在分析代码时必须小心,一个函数在仅定义和分配时不会创建范围。必须调用它。是这样吗?

3 个答案:

答案 0 :(得分:2)

你说得对,但你自己也很困惑。而不是将功能视为“制造新的范围”,而不是理解真正发生的事情。 Javascript通过查看作用域链来解释变量。如果变量在函数的作用域中不存在,则在闭包的情况下,它将上升到封闭级别的作用域。那部分发生在解释变量时。范围本身是在函数创建时创建的,但是当你感到困惑时,当时范围内没有“捕获”任何东西。它只是增加了一个范围链

这就是为什么把闭包放在循环中是javascript编程中的“问题” - 如果你想在{0}时捕获i的值,你应该放入一个自动执行的匿名函数。功能声明。人们倾向于认为(特别是来自程序语言)外部范围被冻结,并且所有当前值都被传递到新的函数范围,而事实并非如此。

答案 1 :(得分:1)

函数的范围在function object is created时设置,例如:

var fn;
// augment scope chain:
with ({foo: "bar"}) {
  fn = function () { // create function
    return foo;
  };
}​​
// restored scope chain
fn(); // "bar"

在上面的示例中,函数在with块内创建,当前范围正在扩充,以将具有foo属性的对象引入范围链。

在你的第二个例子中,发生相同的事情,onclick处理函数正在自动调用匿名函数中创建,该函数本身已经创建了一个新的词法范围。

在每次循环迭代时自动调用该函数时,i的值被捕获到x变量中的该范围,然后创建onclick处理函数在该范围内,它将能够解决它。

答案 2 :(得分:0)

我不明白你的问题,但我没有看到任何奇怪或错误。

在第一种情况下,形成一个闭包,当调用该事件时,它会创建自己的作用域,并从其外部作用域中获取i的值,由于闭包而维护。但是该代码已经被执行,所以i = 6,因为循环很长,因此,x = 6。

在后一个例子中,它是相同的,你正在创建一个闭包,当事件被调用时,它将从其外部作用域中获取x,由闭包维护,但它已经执行,并且因为它立即运行循环,这次x等于函数运行时的i(创建闭包)。