1

可能重复:
JavaScript 闭包是如何工作的?

我搜索了常见问题解答并查看了示例,但我似乎无法理解为什么这不起作用。我真的很感激任何关于我做错了什么的提示。我要做的就是取一个单词,然后单击按钮,为每个字母一次显示一个图像,拼出单词(并且图像应该淡入/淡出)。这是经典的“for 循环仅显示最后一项”问题,但问题是,控制台正确记录。变量更改,但仅显示最后一个图像。再次感谢任何帮助理解我做错了什么,因为我知道完全掌握这一点很重要。下面的代码(我省略了 HTML,因为它只是一个更新的 div 和一个按钮):

$(document).ready(function () {
  var word = 'abc';

  $('#newWordButton').click(function () {
    function animateLetters() {
      function changeLetter() {
        for (i = 0; i < word.length; i++) {
          var currentLetter = word.charAt(i);
          console.log(currentLetter);
          $('#wordsDiv').fadeOut(1000, function () {
            $('#wordsDiv').replaceWith('<img src = "images/letters/' + currentLetter + '.gif" />');
            $('#wordsDiv').fadeIn(1000);
          });
        }
      }
      setTimeout(changeLetter, 1000);
    }

    animateLetters();
  });
});
4

4 回答 4

0

简短的回答是闭包是由函数的范围定义的,而不仅仅是任何一对尖括号。由于 for 循环不是函数定义,因此它不会形成闭包。

我认为@gpojd 提供了一些可行的代码。但是有更多关于关闭 FAQ的信息,例如:

于 2012-11-19T17:00:43.257 回答
0

您需要currentLetter通过将值作为参数传递给自执行函数来创建闭包。这将在您的函数执行时为您保留该值。

未经测试的例子:

for (i = 0; i < word.length; i++) {
    (function (currentLetter) {
        console.log(currentLetter);
        $('#wordsDiv').fadeOut(1000, function () {
            $('#wordsDiv').replaceWith('<img src = "images/letters/' + currentLetter + '.gif" />');
            $('#wordsDiv').fadeIn(1000);
        });
    )(word.charAt(i));
}
于 2012-11-19T16:56:45.590 回答
0

JavaScript 是函数范围的。每次创建新函数时,它都会:

  1. 获取一个范围对象,该对象包含自己声明的变量(包括参数变量)
  2. 具有对定义它的范围的引用,当您尝试访问函数自身范围中不存在的变量时,该引用在运行时遍历。

因此,查看代码的略微编辑版本(添加var到循环中,因此i不是全局的,删除了animateLetters()包装层):

$(document).ready(function () {
  var word = 'abc';

  $('#newWordButton').click(function () {
    function changeLetter() {
      for (var i = 0; i < word.length; i++) {
        var currentLetter = word.charAt(i);
        $('#wordsDiv').fadeOut(1000, function () {
          $('#wordsDiv').replaceWith('<img src = "images/letters/' + currentLetter + '.gif" />');
          $('#wordsDiv').fadeIn(1000);
        });
      }
    }
    setTimeout(changeLetter, 1000);
  });
});

这是生成的结果范围的样子(用方括号表示范围):

[global] - this is the fallback scope, and how every reference to $ is resolved
| var $
+-[function()] (passed to ready call)
  | var word
  +-[function()] (passed to click call)
    | var changeLetter
    +-[function changeLetter()]
      | var i = 4
      | var currentLetter = 'c'
      +-[function()] (passed to 1st fadeOut call)
      | |
      +-[function()] (passed to 2nd fadeOut call)
      | |
      +-[function()] (passed to 3rd fadeOut call)
        |

当您的 fadeOut 函数被调用时,它们会在访问变量时沿着作用域链查找currentLetter变量,因为它们自己的作用域中没有变量。请注意,它们都可以访问相同的父作用域,因此它们都在访问相同的currentLetter变量,在for循环退出后,该变量将指向'c'.


这是使用@gpojd 的解决方案时范围图底部的样子(但不要忘记添加var i到循环表达式!),在for循环退出后:

[function changeLetter()]
| var i = 4
+-[function()] (IIFE in 1st loop iteration)
| | var currentLetter = 'a'
| +-[function()] (passed to 1st fadeOut call)
|   |
+-[function()] (IIFE in 2nd loop iteration)
| | var currentLetter = 'b'
| +-[function()] (passed to 2nd fadeOut call)
|   |
+-[function()] (IIFE in 3rd loop iteration)
  | var currentLetter = 'c'
  +-[function()] (passed to 3rd fadeOut call)
    |

解决方案是使用立即调用函数表达式(IIFE) 在每次循环迭代中创建一个中间函数范围,它将当前值的字符值传递到它自己范围内i的变量中 - 现在当你的淡出currentLetter调用函数时,它们会currentLetter在 IIFE 创建的函数范围内查找并使用变量。

于 2012-11-19T17:31:02.713 回答
0

关闭不是你问题的一半......

for 循环阻止了任何实际动画...您正在用#wordsDiv图像替换 the ,因此下一张图像不可能存在。

见这里:http: //jsfiddle.net/P8Lz5/

$(document).ready(function() {
    var word = 'pneumonoultramicroscopicsilicovolcanoconiosis';
    $('#newWordButton').click(animateLetters);

    function animateLetters() {
        (function changeLetter(i) {
            if (i == word.length) return;

            var currentLetter = word.charAt(i);
            console.log(currentLetter);

            $('#wordsDiv').empty();
            var img = new Image();
            img.src = 'http://placehold.it/100x100/&text=' + currentLetter;

            img.onload = function() {
                $(img).appendTo('#wordsDiv').hide().fadeIn(1000, function() {
                    $('#wordsDiv img').delay(1000).fadeOut(1000, function() {
                        changeLetter(i + 1);
                    });
                });
            };
        })(0);
    }
});​

或者,如果您希望字母一次淡入一个单词以形成一个单词,请参见此处:http: //jsfiddle.net/P8Lz5/1/

于 2012-11-19T17:18:43.187 回答