Закрытия JavaScript ведут себя неожиданно - PullRequest
2 голосов
/ 24 мая 2019

Когда я выполнил этот код, я ожидал увидеть 100 записанных в консоли 10 раз (исходя из опыта использования других языков):

const arr = [];
for (let i = 0; i < 10; i++)
    arr.push(() => i * i);

arr.forEach(f => console.log(f()));

Но я получаю квадрат каждого числа (от 0 до 9). Это, кажется, ведет себя иначе, чем в некоторых других языках.

Например, используя C # , вы получите 100 десять раз:

    var list = new List<Func<int>>();

    for(var i = 0; i < 10; i++)
        list.Add(() => i * i);

    list.ForEach(f => Console.WriteLine(f()));

Python печатает 81 десять раз:

    arr = []
    for i in range(10):
        arr.append(lambda: i * i)

    for f in arr:
        print(f())

Java не допускает этого, поскольку захваченные переменные не могут изменяться.

См. this для примеров на других языках.

Как объяснил Марк ниже, когда мы используем let (область видимости блока) в JavaScript, значение i фиксируется в новой переменной области действия в каждой анонимной функции. Когда вы посмотрите на фактическое определение каждой функции (используя: arr.forEach (f => console.dir (f)); ), вы увидите, что есть область действия (_loop), из которой значение i фиксируется на каждой итерации:

  [[Scopes]]: Scopes[3]
  0: Closure (_loop) {i: 0}
  [[Scopes]]: Scopes[3]
  0: Closure (_loop) {i: 1}
   and so on...

Если мы повторно запустим этот пример, используя var , неудивительно, что область действия уровня блока _loop отсутствует, и вместо этого я сохраняется в области уровня модуля / файла с тем же значением (10) для каждого итерация:

  [[Scopes]]: Scopes[2]
  0: Closure (./src/index.js) {i: 10}

1 Ответ

3 голосов
/ 24 мая 2019

Это характер использования let для объявления переменной в javascript - она ​​ограничена (подразумевается здесь) for блоком цикла, что означает, что каждая итерация получает новое имя в области, которое захватывается в каждом закрытии. Не все замыкания используют одно и то же имя, как это было бы в некоторых других языках, или в JS при использовании var для объявления переменной.

Чтобы получить ожидаемое поведение, используйте var:

const arr = [];
for (var i = 0; i < 10; i++)
    arr.push(() => i * i);

arr.forEach(f => console.log(f()));

Приведенный выше код - проклятие многих запутанных программистов, и это одна из причин, по которой приятно иметь возможность использовать let.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...