JavaScript匿名函数访问局部变量:FileReader读文件报错求助
解决FileReader循环中闭包导致的变量引用问题
这个问题是典型的闭包在循环中的变量捕获陷阱,我来给你拆解一下原因和解决办法:
问题根源
FileReader的onload是异步回调函数,当它触发执行的时候,外层的for循环已经完成了所有迭代。因为你用var声明的x是函数级作用域变量,整个循环共享同一个x引用,这时候x的值已经变成了files.length,files[x]自然就指向了不存在的元素,导致报错。
解决方案
方案1:使用ES6的let声明循环变量(最简洁)
let有块级作用域特性,每次循环迭代都会创建一个独立的x绑定,确保回调函数捕获的是当前迭代的变量值:
var message = document.getElementById("message"); function upFiles(files){ // 将var替换为let,创建块级作用域的变量 for(let x=0; x< files.length; x++){ var fReader = new FileReader(); fReader.onload = function(e){ message.innerHTML += '<br>'+ files[x].name; message.innerHTML += '<br>'+e.target.result; } fReader.readAsText(files[x]); } }
方案2:立即执行函数(IIFE)创建独立作用域(ES5兼容)
如果需要兼容ES5环境,可以用立即执行函数为每个迭代创建独立作用域,捕获当前的文件对象:
var message = document.getElementById("message"); function upFiles(files){ for(var x=0; x< files.length; x++){ // 用IIFE包裹,传入当前迭代的file,形成独立作用域 (function(file){ var fReader = new FileReader(); fReader.onload = function(e){ message.innerHTML += '<br>'+ file.name; message.innerHTML += '<br>'+e.target.result; } fReader.readAsText(file); })(files[x]); } }
方案3:使用forEach遍历数组(可读性更高)
利用Array.prototype.forEach的特性,它的回调函数会为每个元素创建独立的作用域,避免闭包陷阱:
var message = document.getElementById("message"); function upFiles(files){ // 将FileList转为数组后执行forEach遍历 Array.from(files).forEach(function(file){ var fReader = new FileReader(); fReader.onload = function(e){ message.innerHTML += '<br>'+ file.name; message.innerHTML += '<br>'+e.target.result; } fReader.readAsText(file); }); }
补充说明
- 方案1是目前最推荐的写法,现代浏览器都支持ES6语法,代码简洁直观;
- 方案2适合需要兼容旧版浏览器的场景;
- 方案3的代码可读性更强,更符合函数式编程的风格。
内容的提问来源于stack exchange,提问作者Varuni Punchihewa
相关产品推荐
相关产品推荐

