【问题标题】:How do I pass the value (not the reference) of a JS variable to a function? [duplicate]如何将 JS 变量的值(而不是引用)传递给函数? [复制]
【发布时间】:2011-02-03 20:48:54
【问题描述】:

这是我尝试运行的简化版本:

for (var i = 0; i < results.length; i++) {
    marker = results[i];
    google.maps.event.addListener(marker, 'click', function() { 
        change_selection(i);
    }); 
}

但我发现每个侦听器都使用 results.length 的值(for 循环终止时的值)。如何添加侦听器,以便每个侦听器在添加时都使用 i 的值,而不是对 i 的引用?

【问题讨论】:

    标签: javascript closures pass-by-reference listener pass-by-value


    【解决方案1】:

    在现代浏览器中,您可以使用 letconst 关键字来创建块范围变量:

    for (let i = 0; i < results.length; i++) {
      let marker = results[i];
      google.maps.event.addListener(marker, 'click', () => change_selection(i));
    }
    

    在较旧的浏览器中,您需要创建一个单独的作用域,通过将变量作为函数参数传递来将其保存在当前状态:

    for (var i = 0; i < results.length; i++) {
      (function (i) {
        marker = results[i];
        google.maps.event.addListener(marker, 'click', function() { 
          change_selection(i);
        }); 
      })(i);
    }
    

    通过创建一个匿名函数并使用变量作为第一个参数调用它,您可以将值传递给函数并创建一个闭包。

    【讨论】:

    • 您需要在marker 之前添加var,以免污染全局命名空间。
    • @ThiefMaster:奇怪的是,我在第一次看到这个答案后就想到了同样的事情。但是,查看 OP 的代码,我们不能完全确定 marker 还不是全局变量。
    • 使用了谷歌的地图 API,我们可以放心地打赌,标记的范围在 for 循环之外。很好地抓住了安迪。
    • 我同意这种方法有效,但 JSLint 反对在循环内创建函数。您可以在循环之外创建函数,正如 James Allardice 在 jslinterrors.com/dont-make-functions-within-a-loop 中展示的那样
    • @John:JSLint 过分热心的警告之一,IMO。遵守 Crockford 编写 JavaScript 的法则是完全可选的,这就是为什么我使用 JSHint 并带有大多数警告,假设我可能不理解我正在编写的代码已关闭。可悲的是,这是几周内第二次有人在我的一个答案中提出这个问题,但幸运的是,你并没有因此而否决我,以迫使其他人遵守 Crockford 的编码理念. ;-)
    【解决方案2】:

    除了闭包,你还可以使用function.bind

    google.maps.event.addListener(marker, 'click', change_selection.bind(null, i));
    

    在调用时将i 的值作为参数传递给函数。 (null 用于绑定this,在这种情况下您不需要。)

    function.bind 由 Prototype 框架引入,并已在 ECMAScript 第五版中标准化。在浏览器都原生支持之前,您可以使用闭包添加自己的 function.bind 支持:

    if (!('bind' in Function.prototype)) {
        Function.prototype.bind= function(owner) {
            var that= this;
            var args= Array.prototype.slice.call(arguments, 1);
            return function() {
                return that.apply(owner,
                    args.length===0? arguments : arguments.length===0? args :
                    args.concat(Array.prototype.slice.call(arguments, 0))
                );
            };
        };
    }
    

    【讨论】:

    • 刚刚注意到这一点,+1。我是bind 的忠实粉丝,我已经等不及原生实现推出了。
    • 哪些浏览器支持这个?任何移动浏览器?
    • @NoBugs:当前:IE9+。 Fx4+,最近的 Chrome 和 Opera 版本。不是 Safari,不是 iPhone,Android 浏览器从 Ice Cream Sandwich 开始就有了。
    【解决方案3】:

    闭包:

    for (var i = 0, l= results.length; i < l; i++) {
        marker = results[i];
        (function(index){
            google.maps.event.addListener(marker, 'click', function() { 
                change_selection(index);
            }); 
        })(i);
    }
    

    编辑,2013 年: 这些现在通常称为IIFE

    【讨论】:

    • 这里没有错误,但是-1只是因为Andy E先到了那里并进行了更多解释;这个答案不会向页面添加任何内容。
    • 我不确定你是否理解投反对票的原因。除了安迪的(优秀)答案之外,这个答案确实增加了信息:IIFE。
    【解决方案4】:

    您即将关闭。 Here's an article on closures 以及如何与他们合作。查看页面上的示例 5;这就是你正在处理的场景。

    编辑:四年后,该链接已失效。上述问题的根源在于for 循环形成闭包(特别是在marker = results[i] 上)。当marker 被传递到addEventListener 时,您会看到闭包的副作用:共享的“环境”随着循环的每次迭代而更新,然后在最终迭代之后通过闭包最终“保存”。 MDN explains this very well.

    【讨论】:

      【解决方案5】:
      for (var i = 0; i < results.length; i++) {
          marker = results[i];
          google.maps.event.addListener(marker, 'click', (function(i) {
              return function(){
                  change_selection(i);
              }
          })(i)); 
      }
      

      【讨论】:

      • 如果您解释了它的工作原理,这将是一个更好的答案。
      【解决方案6】:

      我想我们可以定义一个临时变量来存储 i 的值。

      for (var i = 0; i < results.length; i++) {
       var marker = results[i];
       var j = i;
       google.maps.event.addListener(marker, 'click', function() { 
         change_selection(j);
       }); 
      }
      

      我还没有测试过。

      【讨论】:

      • 这行不通的原因是 JavaScript 缺少块级作用域。所有范围都是功能级别的。您只能通过调用函数来创建新范围,这是我们在其他答案中看到的。如果不为循环的每次迭代调用函数,就无法为每个 map-event-listener 回调提供不同的闭包。每当您使用像 $.each()_.each() 这样的迭代助手时,这个问题都会为您透明地解决。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-11-10
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 2018-03-05
      • 2020-03-09
      • 1970-01-01
      相关资源
      最近更新 更多