Причина разницы в первую очередь здесь:
var image = new Image();
var context = canvasDisp.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
}
Что делает, это создает замыкание и присваивает его событию load
изображения.Закрытие закрывается по переменным context
и image
(и нескольким другим).Ключевым моментом здесь является то, что он имеет постоянную ссылку на эти переменные, а не копию их значений при создании функции.Поскольку приведенный выше код находится в цикле, все создаваемые функции (замыкания) будут ссылаться на те же context
и image
- последние, созданные циклом.
Это непроисходит в версии each
, потому что в этой версии замыкание закрывается по переменной, которая не изменяется, one context
и image
, созданным при вызове функции итератора, которую выпереход к each
.
Подробнее о замыканиях здесь: Замыкания не сложны
Возможно немного не по теме, возможно, нет: это могло бы быть более ясным, если бы ваши var
были в другом месте.var
иногда неправильно понимают. В частности, не такое же, как объявления переменных в некоторых других языках (например, C, C ++, Java и C #), частично потому, что JavaScript необласть действия уровня блока (только область уровня функции и глобальная область).
Вот как интерпретатор JavaScript видит вашу первую версию:
var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "- disp");
canvasDisp = canvasDispJq.get(0);
image = new Image();
context = canvasDisp.getContext("2d");
image.onload = function() {
context.drawImage(image, 0, 0);
};
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
}
}
Обратите внимание, что все var
операторов переместились наверх, потому что именно так они будут обрабатываться.Независимо от того, где var
появляется в функции, оно вступает в силу с самого начала функции и происходит только один раз.Если var
имеет инициализатор, это становится назначением, где var
.Таким образом, var x = 2;
- это на самом деле две совершенно разные вещи, которые обрабатываются интерпретатором в разное время: var x
, который происходит при входе в функцию до того, как что-либо еще произойдет, и x = 2;
, который происходит там, где он появляется напошаговое выполнение кода функции.
(Кроме того, не по теме: нет необходимости в ;
после закрытия }
цикла for
; но вам нужен один послеприсваивание обработчику onload
. Я сделал оба этих мода также aboev.)
Пишем код так, как его увидит интерпретатор, теперь (во всяком случае, для меня) становится понятнее, что каждый иззамыкания, которые вы назначаете для onload
, будут ссылаться на те же самые context
и image
переменные, и поэтому они все будут видеть последние.
Если выЕсли вы хотите использовать не-1062 * версию, вам просто нужно немного ее изменить, чтобы закрытия onload
закрывали свои собственные копии context
и image
.Это выглядит так:
var ix, iy, canvasDispJq, canvasDisp, image, context;
for (ix = visibleRange.firstX; ix <= visibleRange.lastX; ix++) {
for (iy = visibleRange.firstY; iy <= visibleRange.lastY; iy++) {
canvasDispJq = $("canvas#" + frameResId + "-" + ix + "-" + iy + "- disp");
canvasDisp = canvasDispJq.get(0);
image = new Image();
context = canvasDisp.getContext("2d");
image.onload = createOnloadHandler(context);
image.src = self.baseURL + '/' + canvasDispJq.attr("id");
}
}
}
function createOnloadHandler(ct, img) {
return function() {
context.drawImage(img, 0, 0);
};
}
Теперь замыкание, созданное функцией createOnloadHandler
, закрывается по ct
и img
, а не context
и image
.Каждое закрытие получает свою собственную копию (переданную в вызов createOnloadHandler
, который создал это закрытие).