3

我正在 mozilla.org上研究 Javascript 中的闭包。有一个部分叫做常见错误,这让我大吃一惊。我不知道它到底在说什么。

代码如下,

<p id="help">Helpful notes will appear here</p>
<p>E-mail: <input type="text" id="email" name="email"></p>
<p>Name: <input type="text" id="name" name="name"></p>
<p>Age: <input type="text" id="age" name="age"></p>
function showHelp(help) {
  document.getElementById('help').innerHTML = help;
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function() {
      showHelp(item.help);
    }
  }
}

setupHelp(); 

文本出现在帮助中

只会显示最后一个帮助“你的年龄(你必须超过 16 岁)”,解决方案是在 setupHelp 之外重写 showHelp 函数

function makeHelpCallback(help) {
  return function() {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
      {'id': 'email', 'help': 'Your e-mail address'},
      {'id': 'name', 'help': 'Your full name'},
      {'id': 'age', 'help': 'Your age (you must be over 16)'}
    ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp(); 

我不明白它解决问题的机制。由于我从不使用javascript访问DOM,而是使用jQuery,所以我重写了jQuery中的函数

function makeHelpCallback(i) {
    return function () {
        $("#help").text(i);
    };
}
(function () {
    var helpText = [
    { 'id': 'email', 'help': 'Your e-mail address' },
    { 'id': 'name', 'help': 'Your full name' },
    { 'id': 'age', 'help': 'Your age (you must be over 16)' }
    ];

    for (var i = 0; i < helpText.length; i++) {
        var item = helpText[i];
        $("#"+item.id).focus(function () {
            new makeHelpCallback(item.help)();
        });
    }
})();

但是错误并没有消失。据我所知,jQuery 没有 onfocus 处理程序,不可能将处理程序绑定到同一级别的侦听器。它必须在 function(){} 下。而且我无法达到与 Javascript 相同的效果。

那么无论如何要编写与Javascript代码等效的代码吗?

正如解释所说,它不起作用,因为听众共享相同的环境。不管是什么意思,onfocus = function(){showHelp(item.help)} 分别执行了三次。他们应该受到相应的约束。为什么他们会出现相同的情况?因为 showHelp 是一个静态对象?如果是这样,添加一个新的修饰符应该可以完成工作。并且 makeHelpCallback(item.help) 也共享相同的环境。但它有效。为什么为什么为什么???

为了您的测试方便,附上完整的 HTML 代码。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <p id="help">Helpful notes will appear here</p>
    <p>E-mail:
        <input type="text" id="email" name="email"></p>
    <p>Name:
        <input type="text" id="name" name="name"></p>
    <p>Age:
        <input type="text" id="age" name="age"></p>

    <script src="Scripts/jquery-1.9.1.min.js"></script>

    <script>
        function makeHelpCallback(i) {
            return function () {
                $("#help").text(i);
            };
        }
        (function () {
            var helpText = [
            { 'id': 'email', 'help': 'Your e-mail address' },
            { 'id': 'name', 'help': 'Your full name' },
            { 'id': 'age', 'help': 'Your age (you must be over 16)' }
            ];

            for (var i = 0; i < helpText.length; i++) {
                var item = helpText[i];
                $("#" + item.id).focus(function () {
                    makeHelpCallback(item.help)();
                });
            }
        })();
    </script>
</body>
</html>
4

4 回答 4

3

我会在下面给你答案,但我建议你试着自己弄清楚。一些提示:

你到底传递给该focus方法的是什么?

你的函数的变量是如何定义的?请记住,您需要了解函数何时执行。

在您的代码中:

$("#"+item.id).focus(function () {
        new makeHelpCallback(item.help)();
    });

将函数传递给focus,稍后(当焦点事件发生时)调用此函数,在循环完成并将item变量设置为最后一项之后。你应该做的是传递一个函数,它有自己的局部变量,在你调用时绑定focus。这就是重点makeHelpCallback!你的代码应该是这样的:

 $("#"+item.id).focus(makeHelpCallback(item.help));

makeHelpCallback返回一个函数,在这里,如果这对您来说更清楚,请将其全部内联:

$("#"+item.id).focus((function (i) {
    // this function is executed immediately
    // i is bound to the correct item.
    return function () {
        $("#help").text(i);
    };
})());
于 2013-04-16T13:41:46.503 回答
1

一个简单的例子更容易理解:

var i;
var funcsToRun = [];
for (i = 0; i < 3; ++i) {
  funcsToRun.push(function() { alert(i); });
}
funcsToRun[0](); // "3"
funcsToRun[1](); // "3"
funcsToRun[2](); // "3"

这是因为警报使用的“i”是闭包的一部分,并且被粘在变量“i”上,在您执行函数时,该变量的值是 3。但:

var i;
var funcsToRun = [];
for (i = 0; i < 3; ++i) {
  funcsToRun.push(
    (function(idx) {
      return function() { alert(idx); };
    })(i)
  );
}
funcsToRun[0](); // "0"
funcsToRun[1](); // "1"
funcsToRun[2](); // "2"

之所以有效,是因为不是将警报附加到变量“i”,而是将其附加到“idx”,它立即被评估为循环内的“i”的值,而不是在它之后。所以内部函数立即运行,但只是返回另一个函数alert(0)而不是alert(i)

使用这种思路,您可以将其应用于更大的示例。

于 2013-04-16T13:43:09.550 回答
0

感谢乔的简化示例。我想我可以掌握它。我试着把乔的例子放到程序序列中。让我知道我错了。

var i;
var funcsToRun = [];

//for loop begins
i = 0
//i = 0
funcsToRun.push(function() { alert(i); }); // function(){ alert(i)} is not executed. i remains i.
i++ //i = 1
funcsToRun.push(function() { alert(i); });
i++ //i = 2
funcsToRun.push(function() { alert(i); });
i++ //i = 3 loop stopped. although the for loop completes, i remains in memory. It is not destroyed albeit its completion.

funcsToRun[0](); // function(){alert(i)} executes this moment, and i is 3 now.
funcsToRun[1](); // "3"
funcsToRun[2](); // "3"
于 2013-04-17T05:09:59.720 回答
0

最后我用 jQuery 实现了结果

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
</head>
<body>
    <p id="help">Helpful notes will appear here</p>
    <p>
        E-mail:
        <input type="text" id="email" name="email">
    </p>
    <p>
        Name:
        <input type="text" id="name" name="name">
    </p>
    <p>
        Age:
        <input type="text" id="age" name="age">
    </p>

    <script src="Scripts/jquery-1.9.1.min.js"></script>

    <script>

        (function () {
            var helpText = [
            { 'id': 'email', 'help': 'Your e-mail address' },
            { 'id': 'name', 'help': 'Your full name' },
            { 'id': 'age', 'help': 'Your age (you must be over 16)' }
            ];

            for (var i = 0; i < helpText.length; i++) {
                var item = helpText[i];
                $("#" + item.id).focus(function (msg) {
                    return function () { $("#help").text(msg); }
                }(item.help));
            }
        })();
    </script>
</body>
</html>

谢谢乔和大卫

于 2013-04-17T05:56:55.443 回答