Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Объясните, почему функция работает именно так. (https://javascript.ru/forum/misc/31048-obyasnite-pochemu-funkciya-rabotaet-imenno-tak.html)

Kivi 04.09.2012 02:18

Опять хрень!!! Только, кажется, уже все - разобрался и опять...
Здесь (на http://learn.javascript.ru Использование замыканий есть пример:
function makeArmy() {
 
  var shooters = [];
 
  for(var i=0; i<10; i++) {
    var shooter = function() { // функция-стрелок
      alert(i); // выводит свой номер
    };
    shooters.push(shooter);
  }
 
  return shooters;
}
 
var army = makeArmy();
 
army[0](); // стрелок выводит 10, а должен 0
army[5](); // стрелок выводит 10...
// .. все стрелки выводят 10 вместо 0,1,2...9
ну и объяснение
К моменту вызова army[0](), функция makeArmy уже закончила работу. Цикл завершился, последнее значение было i=10.
Все понятно.

А вот чуть ниже чем тут. Еще пример:
for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}
и тоже объясняется:
Данный код не будет выводить числа с 0 до 9, вместо этого число 10 будет выведено десять раз.

Анонимная функция сохраняет ссылку на i и когда будет вызвана функция console.log, цикл for уже закончит свою работу, а в i будет содержаться 10.


Моё объяснение (по методу прогиба подреальность):
скрипт прошел - все ссылки определились, и не смотря, что в сценарии вывод идет параллельно циклу, на самом деле все не так - вывод в консоль идет в конце.
Вопрос:
Где почитать когда события по коду идут "одновременно" а когда "по-особенному"?

Kivi 04.09.2012 02:22

Nerv
Чувствую, что нужно переходить на ru-board.com - там движение побольше, а не один ты будешь за всех отдуваться.

Aetae 04.09.2012 02:46

function top(){
	for(var i = 0; i < 10; i++) {
		setTimeout(function() {
			console.log(i);
		}, 1000);
	}
}

Тут всё просто, присмотритесь внимательнее:
function() {
    console.log(i);
}
console.log(i) выводит не конкретное число, а внутреннюю переменную i из области видимости функции top(или глобальной если цикл находится вне функции). А поскольку это происходит через 1000мс(1с), когда функция top уже отработала, то обращаясь к i функция получает значение внутренней переменной i, которое та приобрела к концу работы функции top, т.е. - 10.

Разложить это можно ещё так:
for(var i = 0; i < 10; i++) {}

//спустя 1000мс

console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);
console.log(i);



По поводу ru-board.com не скажите, жабаскрипетров нам не так много, а компетентных и подавно.)

Kivi 04.09.2012 13:22

А почему так с кодом
for(var i = 0; i < 10; i++) {}
 
//спустя 1000мс
 
console.log(i);
console.log(i);
console.log(i);
console.log(i);
ведь (из моего примера) он в теле цикла в {}.

Разве не должно быть так:
for i=1 console.log(1);
for i=2 console.log(2);
...
for i=10 console.log(10);
поледовательно ?

nerv_ 04.09.2012 13:54

Kivi, потому, что значение i нигде не фиксируется. Это можно прочитать как: вывести значение переменной i через n миллисекунд. А через n миллисекунд оно будет равно чему?

Цитата:

Сообщение от Kivi
Чувствую, что нужно переходить на ru-board.com - там движение побольше, а не один ты будешь за всех отдуваться.

не то, чтобы я отдувался :) Отвечаю тогда, когда мне удобно. Не хочу, не отвечаю. :no:
Переходить или нет, решать тебе. Могу только отметить, что здесь js мозги очень даже неплохие (это я не о себе).

Kivi 04.09.2012 16:23

Так вот я и хочу узнать механизм.
Внес некоторые изменения в код:
for ( var k = 0; k < 10999; k++ ) {
    document.write( k )
    document.write( "<br>" );
    setTimeout(function() {
        document.write( k );
    }, 2000 );
}
чтоб цикл не успел закончиться, а например document.write(1) должен бы уже напечататься. А все равно ничего.

ведь setTimeout идет как тело цикла и пока оно не выполнится не k не должно меняться.
Или какое k<XXXXXXX нужно поставить чтоб заработало?

Aetae 04.09.2012 17:30

document.write нельзя использовать в функциях исполняемых не сразу, а по событию или таймеру.

document.write - это особый способ вывода, который выводит код по мере загрузки страницы напрямую в тело(т.е. ещё не живой DOM, а просто в обрабатываемый на ходу код), и после того как страница сформирована и документ закрыт его использовать нельзя.

Т.е. document.write можно использовать даже так:
<script>
document.write('<in');
document.write('pu');
document.write('t');
document.write(' valu');
document.write('e="t');
document.write('ext');
document.write('">');
</script>
в отличие от innerHTML который работает уже на живую, а потому каждая выводимая часть обрабатывается сразу как полноценная:
<script>
function appendHTML( html, node ){
	var d = document,
		fragment = d.createDocumentFragment(),
		container = d.createElement('div');
	container.innerHTML = html;
	while( container.hasChildNodes() ) fragment.appendChild( container.firstChild );
	(node || d.body || d.documentElement).appendChild( fragment );
	return node
}
appendHTML('<in');
appendHTML('pu');
appendHTML('t');
appendHTML(' valu');
appendHTML('e="t');
appendHTML('ext');
appendHTML('">');
</script>


Цитата:

Сообщение от Kivi (Сообщение 202949)
Или какое k<XXXXXXX нужно поставить чтоб заработало?

Я так понимаю в хотите подобрать такое k, чтобы на момент прошествия 1000мс цикл всё ещё продолжал исполнение и соответственно значение k было взято из текущей итерации?
Так вот - это не возможно, т.к. асинхронность в js виртуальна и всё исполняется в одном потоке. Вызов же функции по событию или по таймеру, в случае если другой код ещё выполняется, ставит эту функцию в очередь выполнения сразу после окончания текущего непрерывного блока кода. Циклы же относятся как раз тем блокам, что нельзя прерывать.
Соответственно сколько бы не итерировался цикл, функция по таймеру не будет вызвана пока оный не закончится. По этому в описании setTimeout всегда пишут не: "вызывает функцию через n", а "вызывает функцию не раньше чем через n", или типа того.

Цитата:

Сообщение от Kivi (Сообщение 202926)
ведь (из моего примера) он в теле цикла в {}.

Нет не в теле. В описание setTimeout написано.(покрайней мере должно быть), что он исполняете передаваемую функцию в глобальной области видимости. А внутреннюю переменную i она видит только потому, что сама передаваемая функция была создана в внутри другой и замкнула на себя ссылки на внутренние переменные. И на момент исполнения i уже давно равна 10.

Также возможно вас смущает сам синтаксис for? Чтоб было понятнее:
function top(){
    for(var i = 0; i < 10; i++) {
        //...
    }
}
это то же самое что:
function top(){
    var i = 0;
    while(i < 10) {
        i++;
        //...
    }
}
Т.е. i в цикле не имеет никаких особых свойств, это такая же обычная переменная.


Часовой пояс GMT +3, время: 00:38.