我正在尝试将两个变量传递给filereader
回调。基本上我是循环文件并显示它们的缩略图;要做到这一点,我需要两个变量(在这种情况下,parent
和canvas
)。下面的代码有效,但我想确定我未来的自我不会恨我现在的自我。
我假设将两个变量传递到回调函数的范围而不覆盖this
是一种相当常见的情况。
此代码使用名称空间(parent),可以避免,但我仍然想知道如何将两个变量传递给回调函数。
reader.onload = (function (parent) { // even funkier closure to access the parent, the canvas, and the file
return function(canvas) { // funky closure to be able access the canvas and file
return function(r) {
canvas.sourceImage.src = r.target.result;
parent.functions.render(canvas);
parent.functions.preview(canvas, canvas.sourceImage.width/10, canvas.sourceImage.height/10);
};
}(parent.canvases[j]);
})(parent);
reader.readAsDataURL(file);
每条评论,这是完整的代码。我并不是特别想要代码审查(there's a stack for that!
),但我们非常感谢您的反馈。
(function () {
this.readFiles = function (parent) {
var files = parent.fileInput.files;
var j = parent.canvases.length; // so we don't overwrite old previews
for (var i = 0; i < files.length; i++, j++) {
var file = files[i];
var reader = new FileReader;
var canvas = document.createElement('canvas');
canvas.id = "previewCanvas" + j;
canvas.className = parent.canvasClasses;
canvas.width = 100;
canvas.height = 100;
parent.previewWrapper.appendChild(canvas);
/* Initialize Canvases for each file */
parent.canvases[j] = this.create(document.getElementById('previewCanvas' + j));
reader.onload = (function (parent) { // even funkier closure to access the parent, the canvas, and the file
return function(canvas) { // funky closure to be able access the file
return function(r) {
canvas.sourceImage.src = r.target.result;
parent.functions.render(canvas);
parent.functions.preview(canvas, canvas.sourceImage.width/10, canvas.sourceImage.height/10);
};
}(parent.canvases[j]);
})(parent);
reader.readAsDataURL(file);
}
}
}).apply(Canvas.functions);
答案 0 :(得分:1)
基本上你也可以摆脱大部分的闭包和其他一些代码。
除非我已经介绍过任何错误,否则这应该可以正常工作......并且你注意到的两个错误现在已经解决了,感谢抓住它们! : - )
function forEach( array, callback ) {
Array.prototype.forEach.call( array, callback );
}
Canvas.functions.readFiles = function( parent ) {
forEach( parent.fileInput.files, function( file ) {
var reader = new FileReader;
var canvas = document.createElement( 'canvas' );
canvas.id = 'previewCanvas' + parent.canvases.length;
canvas.className = parent.canvasClasses;
canvas.width = 100;
canvas.height = 100;
parent.previewWrapper.appendChild( canvas );
var fileCanvas = Canvas.functions.create( canvas );
parent.canvases.push( fileCanvas );
reader.onload = function() {
fileCanvas.sourceImage.src = reader.result;
parent.functions.render( fileCanvas );
parent.functions.preview(
fileCanvas,
fileCanvas.sourceImage.width / 10,
fileCanvas.sourceImage.height / 10
);
};
reader.readAsDataURL( file );
}
};
一些注意事项:
您不需要将Canvas.functions
变为this
的最外层IIFE。只需直接使用Canvas.functions
即可。这也消除了this
在回调中出现错误的可能性(在这里不会出现问题,但它有助于保持简单)。
您不需要j
变量。 parent.canvases.length
始终与j
的值相同。
如果使用.forEach()
而不是for
循环,则会在.forEach()
回调中声明的所有局部变量上获得闭包。这真的是你需要的唯一关闭。
FileReader
这样的对象可能没有.forEach()
方法。有两种方法可以解决这个问题。一种是编写一个普通的for
循环并在其中调用一个函数,例如
var files = parent.fileInput.files;
for( var i = 0; i < files.length; i++ ) {
doFile( files[i] );
}
function doFile( file ) {
...
}
这可以通过内联函数表达来完成,但我喜欢使用单独的命名函数语句来实现这种用法 - 它看起来更清晰一点。 (这里要注意的是不要将函数语句本身放在嵌套块中,如if
或for
块 - 浏览器不能一致地处理 - 它需要直接在外部函数内部。
或者,使用Array.prototype.forEach.call()
让您在类似数组的对象上使用forEach
。我通过定义独立的forEach()
函数在上面的更新代码中使用了这种方法。您可以直接将Array.prototype.forEach.call()
内联,但我认为将它包装在函数中会更清晰。当然,如果您正在使用诸如Underscore或LoDash之类的库,那么您可以使用方便的迭代器,如_.each()
。
您不需要使用document.getElementById()
来获取对当前画布的引用,因为您已在canvas
变量中使用它。
您不需要onload()
回调中的任何嵌套闭包。该函数已经可以访问必要的变量。
代码似乎使用名称canvas
来表示两个截然不同的事情。第一个是您在顶部创建的实际canvas
元素,第二个是Canvas.functions.create(canvas)
的返回值。我将第二个重命名为fileCanvas
以避免混淆。你可以想出一个更好的名字,但不要把它叫做canvas
。
在onload
回调中,r.target
与reader
是同一个对象,因此您可以直接使用它。如果你在这样的事件处理程序中使用了一个参数,我建议你调用它event
(如果你想要一个简短的名字,可以e
),因为这是更习惯的。
答案 1 :(得分:1)
尽管@MichaelGeary主要通过重构代码回答了我的问题,但标题问题的答案是外部函数立即执行,并且可以访问当前变量:
reader.onload = (function (parent) {
var canvas = parent.canvases[j];
// put more variables ad nauseum here.
// They will be passed to the return function without being changed.
return function(r) {
canvas.sourceImage.src = r.target.result;
parent.functions.render(canvas);
parent.functions.preview(canvas, [...]);
};
})(parent);
通过这种方式,您可以在回调中使用可能需要更改的计数器和其他变量。