СЧЕТЧИК, ПРИМИТИВНЫЙ
Давайте определим функции обратного вызова следующим образом:
// ****************************
// COUNTER BEING A PRIMITIVE
// ****************************
function test1() {
for (var i=0; i<2; i++) {
setTimeout(function() {
console.log(i);
});
}
}
test1();
// 2
// 2
После истечения времени ожидания будет напечатано 2 для обоих. Это связано с тем, что функция обратного вызова обращается к значению на основе лексической области действия , где была определена функция.
Чтобы передать и сохранить значение во время определения обратного вызова, мы можем создать замыкание , чтобы сохранить значение до вызова обратного вызова. Это можно сделать следующим образом:
function test2() {
function sendRequest(i) {
setTimeout(function() {
console.log(i);
});
}
for (var i = 0; i < 2; i++) {
sendRequest(i);
}
}
test2();
// 1
// 2
Теперь, что особенного в этом: «Примитивы передаются по значению и копируются. Таким образом, когда определено замыкание, они сохраняют значение из предыдущего цикла».
СЧЕТЧИК, КАК ОБЪЕКТ
Поскольку у замыканий есть доступ к переменным родительской функции через ссылку, этот подход будет отличаться от такового для примитивов.
// ****************************
// COUNTER BEING AN OBJECT
// ****************************
function test3() {
var index = { i: 0 };
for (index.i=0; index.i<2; index.i++) {
setTimeout(function() {
console.log('test3: ' + index.i);
});
}
}
test3();
// 2
// 2
Таким образом, даже если для переменной, переданной в качестве объекта, создано замыкание, значение индекса цикла не будет сохранено. Это показывает, что значения объекта не копируются, тогда как к ним обращаются по ссылке.
function test4() {
var index = { i: 0 };
function sendRequest(index, i) {
setTimeout(function() {
console.log('index: ' + index);
console.log('i: ' + i);
console.log(index[i]);
});
}
for (index.i=0; index.i<2; index.i++) {
sendRequest(index, index.i);
}
}
test4();
// index: { i: 2}
// 0
// undefined
// index: { i: 2}
// 1
// undefined