【问题标题】:JavaScript for loop on files FileReader文件 FileReader 上的 JavaScript for 循环
【发布时间】:2017-12-14 05:39:33
【问题描述】:

这个问题让我心烦意乱。有人能帮我吗? 在我的 html 文件中的 <script> 标记中,我有这个:

window.ondragover = function(e){return false;}
window.ondragenter = function(e){return false;}
window.ondrop = function(e){
    var files = e.target.files || e.dataTransfer.files;
    for (var i = 0, file; file = files[i];i++){
        var img = document.createElement('img');
        img.height = 200;
        img.width = 200;
        img.style.background = 'grey';
        document.body.appendChild(img);
        var reader = new FileReader();
        reader.onload = function(){
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
    }
    return false;
}

但是当我在浏览器上放置几个图像文件时,只有最后一个图像文件被加载并显示在最后一个 img 元素中,其他的保持灰色。

【问题讨论】:

  • 感觉这将是由于您在循环中使用了img。由于reader.onload是异步的,for循环已经完成,img指向最后一个
  • 在第一个循环中,img 被分配给一个新元素,然后它的src 被分配给在同一循环中创建的新readerresults。下一个循环:img var 被重新分配给一个新元素,它的src 被分配给另一个新的readerresult。有错吗?

标签: javascript for-loop filereader


【解决方案1】:

正如@chazsolo 提到的:

感觉这将是由于您在循环中使用了 img 所致。由于 reader.onload 是异步的,所以 for 循环已经完成,img 指向最后一个

您可以通过在循环 (let - MDN) 中使用 let 而不是 var 来解决此问题。这将为每个 imgreader 在循环内提​​供一个块范围,允许异步读取器方法仍然访问来自该特定循环运行的实际值。

window.ondragover = function(e){return false;}
window.ondragenter = function(e){return false;}
window.ondrop = function(e){
    var files = e.target.files || e.dataTransfer.files;
    debugger;
    for (var i = 0, file; file = files[i];i++){
        let img = document.createElement('img');
        img.height = 200;
        img.width = 200;
        img.style.background = 'grey';
        document.body.appendChild(img);
        let reader = new FileReader();
        reader.onload = function(){
            img.src = reader.result;
        }
        reader.readAsDataURL(file);
    }
    return false;
}

更新:var vs let

那么为什么它不像var 那样工作? 我试着用几个实际的例子来解释letvar 的区别。

变量声明,无论它们出现在哪里,都会在任何声明之前进行处理 代码被执行。

这将我们引向下面的例子(不要介意最后的错误,它是由snipped插件产生的):

用 var 声明

/**
In this example, 'a' is declared after it is used. This doesn't matter, as the 
declaration of 'a' will be processed before running the code. This means a is 
initialized with 'undefined' but is valid. Also the scope of a lies within the 
execution context, that's why it is even available outside of the loop. 
**/
console.log("---------");
console.log("Example Declaration var");
console.log("---------");
for (var i = 0; i < 2; i++) {
  console.log(a); // a is declared but undefined on the 1st run, afterwards it get redeclared and owns the value from the last run.
  var a = i;
}
console.log(a); // a is even available out here as still same execution context.
我们看到,在a 的每一次重新声明中,a 之前的值都会被保留。这不是一个新的“实例”。

那么如果我们在循环中使用异步函数会发生什么?

带 var 的异步函数

/**
This example shows you the effects, if you use a async function within a loop. 
As the loop will be executed way under 100 miliseconds (which is the time out 
of the async function), c will have the same value for all exections of the 
async mehtod, which is the value assigned by the last run of the loop.
**/
console.log("---------");
console.log("Example effects async var");
console.log("---------");
for (var i = 0; i < 2; i++) {
  var c = i;
  setTimeout(function() {
    console.log(c); //var does redeclare, therefor c will be modified by the next loop until the last loop.
  }, 100);
}

完全一样,总是相同的输出(适应您的问题,总是相同的 img 元素和文件)

让我们看看let 发生了什么

用 let 声明

/**
In this example, 'b' is declared after it is used with let. let will be processed 
during runtime. This means 'b' will not be declared when used. This is an invalid 
state. let will give a strict context within the loop. It will be not available 
outside. let has a similar behavior as a declaration in Java.
**/
console.log("---------");
console.log("Example Declaration let");
console.log("---------");
for (var i = 0; i < 2; i++) {
  try {
    console.log(b); //b is not declared yet => exception
  } catch (ex) {
    console.log("Exception in loop=" + ex);
  }
  let b = i;
  console.log("Now b is declared:"+b);
}
try {
  console.log(b); // b is not available out here as the scope of b is only the for loop. => exception
} catch (ex) {
  console.log("Exception outside loop=" + ex);
}
console.log("Done");
抛出了很多异常,因为let 具有更具体的范围。这导致更多的有意编码。

最后,我们看看当我们在循环中使用let 和异步函数时会发生什么。

使用 let 的异步函数

/**
This example shows you the effects, if you use a async function within a loop. 
As the loop will be executed way under 100 milliseconds (which is the time out 
of the async function). let declares a new variable for each run of the loop, 
which will be untouched by upcoming runs.
**/
console.log("---------");
console.log("Example effects async let");
console.log("---------");
for (var i = 0; i < 2; i++) {
  let d = i;
  setTimeout(function() {
    console.log(d); //let does not redeclare, therefor d will not be modified by the next loop
  }, 100);
}

结论

在您的示例中,您总是以最后分配的img 元素和最后分配的file 结束。您执行相同操作的次数与数组中唯一最后一个文件的文件相同。

【讨论】:

  • 谢谢!!!它确实奏效了! js 看起来很简单,但令人困惑,我无法理解为什么 var 不起作用))))
  • @Herr Derb 谢谢你这么详细的解释!现在我看到了它是如何工作的。是否正确:每个新函数声明(在每个循环中)都获得对同一个 img 变量的引用,然后(在对函数的实际调用中)分配给 src 属性(同一个 img var)发生?然后函数,返回函数,并在声明时被调用才有意义=) 所以我可以让它与这样的变量一起工作:reader.onload = (function(theImg){return function(e){theImg.src=reader.result;};})(img); 它和let 一样,对吧?
  • 是的,这样您就可以将当前变量值“绑定”到返回函数。如果您想避免使用let,这将是解决方案。
  • 因此,如果我使用一个返回新处理函数的函数,并在循环中以 img var 作为参数调用以创建一个新的本地上下文变量,该变量对于当前循环中返回的处理函数是唯一的,和let 一样,对吗?
  • 我认为var 创造了全局价值,而let 创造了局部价值。可以在这里找到解释:codeburst.io/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-10-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-27
  • 2014-11-07
相关资源
最近更新 更多