【问题标题】:global array returns undefined全局数组返回未定义
【发布时间】:2014-04-05 06:48:35
【问题描述】:

我有一个带有 1、2、3、4 等按钮的幻灯片。我还定义了一个包含 4 个图像 url 的数组。当点击按钮时,它会调用具体对应的url。

<div id="slideshow">
<div id="source"><img src="images/blue.jpg">
<ul id="controller">
<li class="button">1</li>
<li class="button">2</li>
<li class="button">3</li>
<li class="button">4</li>
</ul>
</div>
</div>

Javascript:

var button=document.getElementsByClassName("button")
var image=document.getElementsByTagName("img")[0]
var sources=new Array("images/blue.jpg","images/red.jpg","images/yellow.jpg","images/green.jpg");

for (var i=0;i<sources.length;i++){
    button[i].onclick=function(){
            image.src=sources[i]
            }
}

但是当我点击按钮时,sources[i] 总是返回 undefined。

【问题讨论】:

标签: javascript variables closures


【解决方案1】:
for (var i=0;i<sources.length;i++){
    button[i].onclick= function(i){
       return function(){
            image.src=sources[i]
            }
     }(i);
}

试试这个http://jsbin.com/puyipiro/1/

附: onclick 处理程序中的错误原因 i 等于 4,因此必须使用创建处理程序在每次迭代中保存当前值

【讨论】:

  • 我也想这样用
  • 我们将每次迭代的值传递给函数,因此它在闭包中保存值。
  • var button=document.getElementsByClassName("button") var image=document.getElementsByTagName("img")[0] var sources=new Array("images/blue.jpg","images/ red.jpg","images/yellow.jpg","images/green.jpg");函数 changepic(x) { imarge.src=sources[x] } for (var i=0;i
  • 所有我建议你看另一个答案 - 我也认为,最好将图像编号保存为属性
【解决方案2】:

您遇到了 JavaScript 中最令人困惑的部分之一,即闭包捕获问题。为了让你的代码重新编写,

var button=document.getElementsByClassName("button")
var image=document.getElementsByTagName("img")[0]
var sources=new Array("images/blue.jpg","images/red.jpg","images/yellow.jpg","images/green.jpg");

for (var i=0;i<sources.length;i++){
   (function (i) {
     button[i].onclick=function(){
       image.src=sources[i]
     }
   })(i);
}

这使用一个立即执行的函数来引入一个新的作用域,因此每个onclick 都有自己的i 副本,在创建函数时其值为i,而不是共享相同的i其值为sources.length

如果您的目标浏览器支持 ES 6,您可以使用 let 而不是 var,这样您就可以将其写成,

var button=document.getElementsByClassName("button")
var image=document.getElementsByTagName("img")[0]
var sources=new Array("images/blue.jpg","images/red.jpg","images/yellow.jpg","images/green.jpg");

for (var i=0;i<sources.length;i++){
  let j = i;
  button[i].onclick=function() {
    image.src=sources[j]
  }
}

let 语句为循环中的每次迭代创建一个新的 j 作用域,因此 onclick 函数捕获的 j 是不同的,并存储创建函数时 i 的值就像上面立即执行的函数一样。

不幸的是,ES 6 尚未广泛使用,因此您需要一些我最初建议的变体。

【讨论】:

    【解决方案3】:

    您可以将 i 值存储在适当的 DOM 元素(按钮)中,例如作为属性,然后在onclick 处理程序中检索它们。有点像这样:

    for (var i=0;i<sources.length;i++){
        button[i].setAttribute('data-btn_i', i);
        button[i].onclick=function(){
            image.src=sources[this.getAttribute('data-btn_i')];
        }
    }
    

    【讨论】:

    • 这是一个昂贵的不可组合的解决方案。例如,如果您剪切并粘贴此代码,它将中断,因为两个副本将使用相同的属性。您需要记住更改属性名称。创建闭包更加简洁、快速且可组合。
    • @chuckj 昂贵 - 也许,不可组合 - 不太可能。这两个语句旨在始终彼此相邻。所以我看不出它怎么会破坏任何东西
    • 我理解其意图。但是,它不会改变您实际上使用的是全局变量这一事实。
    • 全局变量?我认为在提及全局变量之前您必须阅读数据属性(developer.mozilla.org/en-US/docs/Web/Guide/HTML/…
    • 请。我明白什么是属性。我没有说你使用了全局变量,我说你使用的技术等同于使用全局变量。
    猜你喜欢
    • 2016-05-13
    • 2018-08-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多