Интересная задача на область видимости в js

На странице есть 10 ссылок, нужно привязать обработчик onclick для каждой ссылки, так что бы при нажатии на нее выводился ее порядковый номер, который присваивается в цикле, решил таким образом
https://jsfiddle.net/0qagg1ek/1/
Решил не с первой попытки, хочу разобраться в деталях как проходил выполнение, я понимаю так:

  • на каждой итерации цикла вызывается функция на месте, с параметром текущей итерации
  • каждой html ссылке, событию onclick присваивается ссылка на функцию обработчик :wink:
  • у каждого обработчика свое значение при вызове алерта благодаря инкапсуляции во внешнюю функцию, что создает для каждой переменной index свою область видимости
    Правильно ли я понимаю работу кода? И хочу услышать(прочитать:laughing:) как вы понимаете тот беспредел в просторах памяти который творит этот js

Это извесная задача. Кстати для неё есть оригинальное решение :smile:. Правда оно не компатибильно с ES5 (strict mode).

https://jsfiddle.net/0qagg1ek/3/

Функция вызывается сразу, чтобы вычислять значение переменной index. Если не оборачивать в функцию, то на выходе из цикла i будет равно 10, а поскольку мы навесили на элемент функцию, которая будет выполнена в будущем, при наступления события click, переменная i будет вычислена в значение 10 и алертом будет выводиться ее значение, увеличенное на 1.

Можно другим способом решить, если немного отойти от условия задачи: https://jsfiddle.net/y33hvb5a/1. В этом случае мы будем привязываться к конкретному индексу элемента, который не меняется, в отличие от переменных.

1 лайк

Да этот вариант работает правильно, но суть вопроса направлена на понимание замыканий

Можно сказать, своя область видимости есть у каждого константного значения переменной index. Секрет фокуса тут не в инкапсуляции, а в замыкании. Потому что наша функция должна была бы выглядеть как-то так примерно:

function(index){
    function(event){
        event.preventDefault();
        alert(index + 1);
    }
} 

Но она так не выглядит, потому что когда она вызывается, никакого index во внешнем мире не существует, а вызывается она из элемента, который был когда-то временно прописан в коллекции linksCollection, и получил там значение переменной index, хотя, кажется, там была целая функция.

Но это только так кажется, потому что функция в памяти существует в одном-единственном экземпляре, каждый onclick получает только ссылку на неё, в которую неявно подмыкается значение переменной index, как будто где-то рядом есть такой волшебный цикл, который в момент нажатия на ссылку чудесно оказывается в нужной итерации, но это мираж, потому что цикл уже отработал и ушёл на пенсию.

Беспредел есть только в голове. А в памяти всё как раз оптимальненько, скорее всего.

2 лайка

Можно переписать Вашу функцию по-другому, если смущает способ решения:

[].slice.call(linksCollection).forEach(function (el, index){
    el.onclick = function(event){
            event.preventDefault();
            alert(index + 1);
        };
})