应用事件侦听器时变量超出范围

时间:2013-10-06 00:06:20

标签: javascript

为什么i超出了此代码中callback function的范围?

// All menu items collection
var menuItems = document.getElementsByClassName('menu-item');

// Loop trough all menu items and attach
// event listeners.
for (var i = 0; i < menuItems.length; i++) {
        // Check if element truely exsists
        if (menuItems[i]) {
            menuItems[i].addEventListener('click', function(e){
                ////////////////////////////////
                // NOTE: i, is out of scope! //
                //////////////////////////////
                var icon = menuItems[i].children[1],
                    submenu = menuItems[i].children[2];

                // Change icon color
                if (icon.style.background !== "blue") {
                    icon.style.background = "blue";
                } else {
                    icon.style.background = "red";         
                }

                // Show/hide submenu
                if (submenu.style.display !== "block") {
                    submenu.style.display = "block";
                } else {
                    submenu.style.display = "none";         
                }
            });
        }
}

1 个答案:

答案 0 :(得分:4)

变量i不在范围之外。只是在循环完成后的某个时间调用事件处理程序,因此变量i的值指向menuItems数组之外。

将代码包装在函数中,以创建一个范围,其中每个迭代都有一个变量的副本:

for (var i = 0; i < menuItems.length; i++) {
  // Check if element truely exsists
  if (menuItems[i]) {

    (function(i){  

      menuItems[i].addEventListener('click', function(e){
        var icon = menuItems[i].children[1],
            submenu = menuItems[i].children[2];

        // Change icon color
        if (icon.style.background !== "blue") {
          icon.style.background = "blue";
        } else {
          icon.style.background = "red";         
        }

        // Show/hide submenu
        if (submenu.style.display !== "block") {
          submenu.style.display = "block";
        } else {
          submenu.style.display = "none";         
        }
      });

    })(i);

  }
}

当事件处理程序使用i变量时,它将在该函数的闭包对象中捕获。每个事件处理程序都有一个单独的闭包对象,但没有函数包装器为每次迭代创建一个单独的i变量,所有闭包都将捕获相同的变量。

函数的闭包包含函数从外部作用域使用的所有局部变量。除非menuItems是全局变量,否则它也将在闭包中。