javascript closure (loop example)

С вопроса "Почему это не работает?" люди обычно начинают изучение замыканий.

by Yury

HTML

<div id="parent"></div>
<br><br>
<div id="parent2"></div>

CSS

.closure-div {
    width: 100%;
    height: 30px;
}

JavaScript

//Функция addEvents принимает массив div'ов и ставит каждому вывод своего номера на onclick.

//Для тестового примера сделаем 10 разноцветных нумерованных div'ов с разными цветами:

makeDivs("parent");

function makeDivs(parentId) {
	for (var i=0; i < 10; i++) {
		var j = 9-i
		var div = document.createElement('div')
		div.style.backgroundColor = '#'+i+i+j+j+j+i
		div.className="closure-div"
		div.style.color = '#'+j+j+i+i+i+j
		document.getElementById(parentId).appendChild(div)
	}
}


function addEvents(divs) {
	for(var i=0; i < divs.length; i++) {	
		divs[i].innerHTML = i
		divs[i].onclick = function() { alert(i) }
	}
}

addEvents(document.getElementById('parent').childNodes);

//Если Вы покликаете на div'ы - они все выдают одинаковый alert.

//Такой глюк возник из-за того, что все функции div[i].onclick получают значение i из одного на всех [[scope]] внешней функции. А это значение ([[scope]].i) на момент активации onclick-обработчика равно 10 (цикл завершился как только i==10).

//Чтобы все было в порядке, в таких случаях применяют специальный прием - выделение [[scope]]. Следующая функция работает правильно. В ней все то же самое, кроме div.onclick.

function addEvents2(divs) {
	for(var i=0; i < divs.length; i++) {	
		divs[i].innerHTML = i
		divs[i].onclick = function(x) {
			return function() { alert(x) }
		}(i)
	}
}

makeDivs("parent2");

addEvents2(document.getElementById('parent2').childNodes);

//Теперь все должно быть в порядке - каждый div дает alert на свой номер.

//Для присваивания div.onclick запускается временная функция function(x) {..}, принимающая аргумент x и возвращающая обработчик, который берет x из [[scope]] этой временной функции.

//Запись function(x) {..} используется для создания функции, и тут же (i) - для запуска с аргументом i.


//Временная функция function(x) {..} заканчивает работать тут же, оставляя в своем [[scope]] правильное значение x, равное текущей переменной i цикла.

//Когда обработчик активизируется - alert возьмет из...