jQuery事件绑定按值传递

jQuery event binding pass by value

本文关键字:按值传递 绑定 事件 jQuery      更新时间:2023-09-26

下面的页面显示了五个按钮,单击每个按钮都会收到警报'5'。我想当我点击第一个按钮时,我得到警报'1',第二个按钮'2'…等等

<!doctype html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js"></script>
    </head>
    <body>
        <div id="nums"></div>
        <script type="text/javascript">
            var nums = [1,2,3,4,5];
            for(var i in nums){
                var num = nums[i];
                var btn=$('<button></button>').text(num).click(function(){alert(num);});
                $('#nums').append(btn);
            }
        </script>
    </body>
</html>

JavaScript只有函数作用域,所以在您的示例中只有一个num,它不断被修改,到调用click事件时,它等于五个。要解决这个问题,必须使用新的函数范围创建正确的闭包,如下所示:

var nums = [1,2,3,4,5];
var createClick = function (num) { return function() { alert(num); }; };
for(var i in nums){
    var num = nums[i];
    var btn=$('<button></button>').text(num).click(createClick(num));
    $('#nums').append(btn);
}  

在循环中创建的事件处理程序函数有一个持久引用到创建它们的作用域内的变量,而不是在创建函数时对这些变量的复制。这就是闭包的工作方式(more: 闭包并不复杂)。所以所有的处理函数都引用同一个num变量,并且它们在click事件发生时引用它。因为在这一点上,它是5(它曾经被分配的最后一个值),你最终与所有它们警告5

如果您希望事件处理程序在创建处理程序时引用num,则必须让处理程序关闭不会更改的内容。通常的方法是使用一个函数来构建处理程序函数,并让处理程序函数靠近该构造函数的参数:

var nums = [1,2,3,4,5];
for(var i in nums){
    var num = nums[i];
    var btn=$('<button></button>').text(num).click(buildHandler(num));
    $('#nums').append(btn);
}
function buildHandler(val) {
    return function(){alert(val);};
}

可以看到,在钩住click事件并将其返回值传递给click时,我们调用 buildHandlerbuildHandler创建并返回一个函数,该函数关闭我们传递给它的参数val。由于val是我们想要警报的数字的副本,并且没有任何东西改变val,因此函数按预期执行。


离题1:你在代码中创建了很多全局变量。全局名称空间已经非常拥挤了,我建议尽可能避免创建更多的全局名称空间。我将代码包装在函数中,这样变量就都是函数的局部变量:

<script>
    (function() {
        var nums = [1,2,3,4,5];
        for(var i in nums){
            var num = nums[i];
            var btn=$('<button></button>').text(num).click(buildHandler(num));
            $('#nums').append(btn);
        }
        function buildHandler(val) {
            return function(){alert(val);};
        }
    })();
</script>

它做同样的事情(我们定义函数,然后立即调用它),但没有创建全局i, num, numsbtn变量。


离题2:您使用for..in与数组没有做任何检查,以确保您只处理数组索引。for..in循环通过数组索引;它循环遍历对象属性名。在编写for..in循环时,就好像它们循环遍历数组索引一样,这在某些阶段会让您感到困惑。只需使用普通的for循环或进行必要的检查,或者使用jQuery的each函数。更多: for..in的神话与现实

改变这一行:

var btn=$('<button></button>').text(num).click(function(){alert($(this).text()});

您创建的所有click处理程序都指向相同的全局变量num,该变量在循环运行后为5,在您实际单击以触发处理程序并显示警报时仍为5。

有两种方法可以绕过它:

(1)如果你已经设置了数字作为按钮的文本,在处理程序中使用它,像这样:

// jQuery will set 'this' for you when it calls your click handler
[...].click(function(){ alert($(this).text()); });

(2)创建一个闭包,使num的单个值为每个处理程序保留:

[...].click((function(closureNum){
                return function(){ alert(closureNum); }
             })(num));
编辑:刚刚看到了其他的答案。为了更好地改变我的第二个选择,我建议做T.J.和FishBasketGordo做的事情,并有一个单独的处理程序构建函数,而不是像我那样内联地做。两者都可以,但是单独的处理程序构建器应该更有效(并且更容易阅读)。

试试这个

<script type="text/javascript">
            var nums = [1,2,3,4,5];
            for(var i in nums){
                var num = nums[i];
                var btn=$('<button></button>').text(num).click(function(){alert(nums[i]);});
                $('#nums').append(btn);
            }
        </script>

这个更简单:

var nums = [1,2,3,4,5];
$.each(nums, function(index, value){
    var btn=$('<button></button>').text(value).click(function(){alert(value);});
    $('#nums').append(btn);
});

既然你已经有jquery在你的页面,为什么不使用$.each()?