Что за вопрос!
Проблема в том, что в javascript, как и в java, целочисленные значения передаются по значению, а не по ссылке. Вот что все остальные здесь говорят вам. Но я предполагаю, что вы не знаете, что это значит. Это довольно стандартно для всех языков, так что давайте посмотрим, смогу ли я объяснить!
ПРИМЕЧАНИЕ: это искаженное объяснение. Я действительно не знаю, как javascript передает значения, и такие забавные термины, как «stack», «heap» и «pass by reference value» (что я считаю технически правильным?) Могут появиться в более академических ответах. Надеюсь, я могу пропустить все это и предположить, что
- есть одно место, где компьютер хранит память для программ (нет)
- он хранится в шестнадцатеричной записи в оперативной памяти (может быть, да? Не знаю)
- мы только передаем по ссылке и передаем по значению (подробнее об этом ... сейчас!)
Итак, у нас есть передача по ссылке и передача по значению.
В памяти программы при передаче по значению переменная «i» указывает на буквальное число.
Когда вы передаете по ссылке, то, на что будет указывать переменная «i», будет ячейкой памяти какого-то другого объекта.
Что это значит?
line 1. i = new Object();
line 2. i = new Object();
после строки 1. программа создаст небольшой «дом» в памяти для «i», и это даст расположение - я не знаю, какое-то значение оперативной памяти (скажем, я действительно не знаю, капюшон javascript). Затем, в этом значении, будет установлено другое значение. Итак имеем:
i -> 0x67 -> 0x68
, поэтому программа знает, что, когда она видит значение «i», она переходит в область памяти 0x67 и получает значение 0x68. Он также знает, что значение ТО указывает на другое место в памяти. В этом случае у нас есть
...
0x67 -> 0x68 (in bytes)
0x68 -> some byte representation of a new Object()
0x69 -> i don't know! some more bytes. they're not important right now.
...
После запуска строки 2 у нас будет
i ->0x67 -> 0x69 (in bytes)
и
0x69 -> some byte representation of a new Object()
дать:
...
0x67 -> 0x68 (in bytes)
0x68 -> some byte representation of a new Object()
0x69 -> some byte representation of a new Object()
...
Теперь, как это происходит, если бы вы были настолько равны в двух новых объектах, вы бы прекрасно, что они одинаковы (я думаю).
Здесь важно отметить, что после второй строки значение i изменилось с первой ячейки памяти на вторую.
ТАК, если вы сделали это:
i = new Object()
j = i
i = new Object()
вы получите:
i -> 0x67 -> 0x68 ->byte for a new Object
j -> 0x69 -> 0x68 -> byte for a new Object
i -> 0x67 -> 0x70 -> byte for another new Object. Same bytes as above.
См? «j» получает действительные значения байтов в «i», что означает, что «j» получает значение 0x68, которое является ячейкой памяти для нового объекта. После третьей строки j имеет местоположение первого объекта, а «i» - местоположение второго объекта.
ОДНАКО, любые изменения, которые вы вносите в j - скажем,
j.x = "moo"
не появится в объекте, на который указывает «i». Он появится только в объекте "j". Потому что, помните, j указывает на совершенно другой объект в совершенно другом месте, чем объект, на который я указываю.
Хорошо, с , что вне пути, давайте вернемся к передаче по значению.
Здесь
i = 6;
j = i;
i = 7;
так мы получаем
i -> 0x67 -> 6 (well, the bytes for 6, anyway)
j -> 0x68 -> 6 (again, bytes for 6. the same bytes. We're pointing to i here)
i -> 0x67 -> 7
И, если бы мы посмотрели на j?
j -> 0x68 -> 6
итак, потому что мы обновили «i», мы также не обновили «j», понятно? Байты хранятся, я получаю свою собственную копию байтов здесь.
ОК, так что со всем вышеперечисленным, давайте проверим ваш код:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i} );
}
return result;
}
верно, поэтому в цикле for вы создаете функцию , которая содержит ссылку на i , и действие цикла for заключается в увеличении значения i.
Вы, вероятно, собираетесь "держись!" или что-то - вы думаете, что внутренне так? - потому что я только что заметил, как данные передаются по значению для целых чисел (или чисел, как правило). Проблема вот в чем:
С этой страницы:
https://developer.mozilla.org/en/JavaScript/Guide/Closures
Закрытие - это особый вид объекта, который объединяет две вещи: функцию и среду, в которой эта функция была создана. Среда состоит из любых локальных переменных, которые находились в области действия во время создания замыкания.
Итак ... и я вставил вышеупомянутое по причине - закрытие - это объект, который является функцией (скажем, fun1) и средой, в которой была создана функция. А окружающая среда? Он состоит из локальных переменных (о, как насчет i?) В области видимости.
Итак, чтобы быть тупым ... так же, как i все еще находится в области действия все время цикла for, то же самое "i" находится в области действия для каждого замыкания, которое мы генерируем. А что я? что я получаю обновленную стоимость. Вы не передаете снимок из i, вы передаете, насколько я могу определить, сам опорный объект. Это больше не передача по значению, мы вроде как возвращаемся по ссылке.
Итак, в памяти у нас есть
i -> 0x67 -> 0
i -> 0x67 -> 1
i -> 0x67 -> 2
i -> 0x67 -> 3
i -> 0x67 -> 4
i -> 0x67 -> 5 (yeah, the loop increments i, then fails the test of i <5. but is still 5)
в вашем массиве вы помещаете функцию, которая просто выплевывает значение i. Но вы увеличиваете я все время. Каждая функция, которую вы помещаете в этот массив, будет выдавать «5».
Чтобы сделать еще один шаг вперед ...
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i++;} );
}
return result;
}
alert(fun1()[0]());
alert(fun1()[0]());
alert(fun1()[4]());
Здесь мы проверяем, можем ли мы «сохранить» изменения за пределами исходной среды. Если вы запустите это, вы не увидите сохраненных изменений. В первом примере мы увеличиваем «i» до 6 - но когда мы снова запускаем пример, я снова равняюсь 5 - приращение не сохраняется.
Наконец, попытка использования другой функции массива показывает, что значение там тоже не увеличилось.
Итак, вам нужно как-то заморозить во времени значение i в момент его поступления в массив
И это довольно сложно.
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( eval("(function() { return " + i+";})"));
}
return result;
}
Где "eval" - это необычная функция javascript, которая создает код для запуска во время его запуска - я имею в виду, динамически. И я окружаю его круглыми скобками, потому что
Почему для eval в JavaScript нужны скобки для оценки данных JSON?
Я не знаю много о eval!
Далее, одно из других решений показывает это:
(function(x){
result.push( function() {return x} );
})(i);
, который помещается в контекст цикла for - чтобы дать
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
(function(x){
result.push( function() {return x} );
})(i);
}
return result;
}
Для этого создается новая среда, в которой существует замыкание. И в этой среде «x» всегда и навсегда фиксируется как значение, которое i в настоящее время (т.е. в точке создания экземпляра). Поэтому, когда наш массив пытается получить доступ к заданному замыканию, он существует в новой среде, где x фиксирован. Если бы мы сделали это вместо этого:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
(function(){
result.push( function() {return i;} );
})();
}
return result;
}
Тогда мы столкнулись с той же проблемой. «I» still существует в среде (или области, если хотите) цикла, поэтому он будет выбран, независимо от того, сколько раз вы вставляете его в функции.
Если кто-то читает это и думает, что я написал глупости, пожалуйста, дайте мне знать! Мне очень нравится слышать, как js работает с управлением памятью.