jQuery 事件绑定按值传递

Posted

技术标签:

【中文标题】jQuery 事件绑定按值传递【英文标题】:jQuery event binding pass by value 【发布时间】:2011-10-13 02:00:45 【问题描述】:

以下页面显示五个按钮,单击每个按钮都会收到警报“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>

【问题讨论】:

感谢大家的出色回答!我为所有正确答案单击了“+1”。但我只能接受一个答案。真的很抱歉。 【参考方案1】:

JavaScript 仅具有函数作用域,因此在您的示例中只有一个 num,它不断被修改,并且在调用 click 事件时,它等于五个。为了解决这个问题,您必须使用新的函数范围创建正确的 closure,如下所示:

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);
  

【讨论】:

你的意思是,为了通过值传递,我必须创建一个闭包?为什么闭包可以实现按值传递?还有其他解决方案吗? 这是一个常见问题。您已经在原始示例中创建了一个闭包,但是num 指的是外部范围中的var num,当您在var num = nums[i]; 行中修改外部范围中的num 时,您正在修改@您的闭包所指的 987654328@。因此,它总是会发出警报 5. 通过使用返回函数的辅助函数,您可以在 numcreateClick 的参数)周围创建一个闭包,该闭包不会被修改。这个解决方案有一些变化,但它们本质上都是相同的。 JavaScript 仅具有函数作用域。 @Peter:这与按值传递无关。 (并且 JavaScript 完全是按值传递;在 JavaScript 中根本没有按引用传递。)【参考方案2】:

改变这一行:

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

【讨论】:

智能回答。但这是一种解决方法,而不是解决方案。 text() 恰好是我们想要的 alert();假设 text() 与我们要警告的内容无关,则该变通方法不起作用。 对,但这不是问题。【参考方案3】:

您在循环中创建的事件处理函数对创建它们的范围内的变量有一个持久引用,而不是这些变量的副本函数创建的时间。这就是闭包的工作原理(更多:Closures are not complicated)。因此,您的所有处理程序函数都引用同一个 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 事件时调用 buildHandler 并将其返回值传递给clickbuildHandler 创建并返回一个关闭 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>

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


题外话 2:您已将 for..in 与数组一起使用,而没有进行任何检查以确保您只处理数组索引。 for..in 确实遍历数组索引;它循环遍历对象属性名称。编写for..in 循环,就好像它们遍历数组索引一样会在某个阶段咬你。只需使用普通的for 循环或进行必要的检查或使用jQuery 的each 函数。更多:Myths and realities of for..in

【讨论】:

+1 获取for..in 描述和链接文章。 (我会说 +2 来奖励你写了链接的文章,但我不会让我做 +2。) +1 获取详细说明。我认为问题可以通过另一种方式解决: 1. deep clone num。 alert(num.deep_clone());但我没有完成实验。我认为该解决方案应该可以工作,至少在 C# 和 python 等其他语言中是这样。 @Peter:当click 触发时,您只需克隆num 变量,这不会改变任何东西(您将克隆值5 )。您必须更早地复制该值,循环创建处理程序期间,这正是我们对 buildHandler 所做的事情。【参考方案4】:

您创建的所有click 处理程序都引用同一个全局变量num,在循环运行后它将为 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 确实并且有一个单独的处理程序构建函数,而不是像我那样内联。两者都应该工作,但单独的处理程序构建器应该更有效(并且可以说更容易阅读)。

【讨论】:

【参考方案5】:

试试这个

<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>

【讨论】:

这不会起作用,原因与原始代码不起作用的原因相同:循环运行后,到实际调用处理程序时,i 将是 4。跨度> 【参考方案6】:

这个比较简单:

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()?

【讨论】:

以上是关于jQuery 事件绑定按值传递的主要内容,如果未能解决你的问题,请参考以下文章

jquery如何绑定一个已有的方法并传递参数

JQuery事件——绑定多事件.on()和卸载事件.off()

jquery绑定事件on()方法

jquery事件绑定

11-jQuery的事件绑定和解绑

jQuery得事件绑定