JavaScript 是函数范围的。每次创建新函数时,它都会:
- 获取一个范围对象,该对象包含自己声明的变量(包括参数变量)
- 具有对定义它的范围的引用,当您尝试访问函数自身范围中不存在的变量时,该引用在运行时遍历。
因此,查看代码的略微编辑版本(添加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 创建的函数范围内查找并使用变量。