Выводит следующее:

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i  ) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: "   i);
  };
}
for (var j = 0; j < 3; j  ) {
  // and now let's run each one to see
  funcs[j]();
}

Мое значение: 3

В то время как я хотел бы, чтобы выход:
В то время как я хотел бы, чтобы выход:
В то время как я хотел бы, чтобы выход:

Принимая во внимание, что я хотел бы вывести:

Мое значение: 0
Мое значение: 1
Мое значение: 2


Та же проблема возникает, когда вызвана задержка в запуске функции с помощью прослушивателей событий:

var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i  ) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: "   i);
  });
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>

… или асинхронного кода, например используя обещания:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for (var i = 0; i < 3; i  ) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

Каково решение этой основной проблемы?

Проблема в том, что переменная i внутри каждой из ваших анонимных функций привязана к одной и той же переменной за пределами функции.

Классическое решение: замыкания

Что вы хотите сделать, это связать переменную внутри каждой функции с отдельным неизменным значением вне функции:

var funcs = [];

function createfunc(i) {
  return function() {
    console.log("My value: "   i);
  };
}

for (var i = 0; i < 3; i  ) {
  funcs[i] = createfunc(i);
}

for (var j = 0; j < 3; j  ) {
  // and now let's run each one to see
  funcs[j]();
}

Поскольку в JavaScript нет области блока — только область функции — Оборачивая создание функции в новую функцию, вы гарантируете, что значение «i» останется таким, как вы предполагали.


Решение 2015: forEach

При относительно широкой доступности функции Array.prototype.forEach (в 2015 г.) стоит отметить, что в тех ситуациях, когда итерация выполняется в основном по массиву значений, .forEach() обеспечивает чистое, естественное способ получить четкое замыкание для каждой итерации. То есть, если у вас есть какой-то массив, содержащий значения (ссылки DOM, объекты и т. Д.), И возникает проблема настройки обратных вызовов, специфичных для каждого элемента, вы можете сделать это:

var someArray = [ /* whatever */ ];
// ...
someArray.forEach(function(arrayElement) {
  // ... code code code for this one element
  someAsynchronousFunction(arrayElement, function() {
    arrayElement.doSomething();
  });
});

Идея состоит в том, что каждый вызов функции обратного вызова, используемой с циклом .forEach, будет собственным закрытием. Параметр, передаваемый этому обработчику, является элементом массива, специфичным для данного конкретного шага итерации. Если он используется в асинхронном обратном вызове, он не будет вступать в конфликт с любыми другими обратными вызовами, установленными на других этапах итерации.

Если вы работаете в jQuery, функция $.each() дает вам аналогичную возможность.


Решение ES6: let

ECMAScript 6 (ES6) вводит новые ключевые слова let и const, которые имеют область действия, отличную от переменных на основе var. Например, в цикле с индексом на основе let каждая итерация цикла будет иметь новое значение i, где каждое значение находится в пределах цикла, поэтому ваш код будет работать так, как вы ожидаете. Существует много ресурсов, но я бы порекомендовал блок-пост 2ality в качестве отличного источника информации.

for (let i = 0; i < 3; i  ) {
  funcs[i] = function() {
    console.log("My value: "   i);
  };
}

Однако остерегайтесь того, что IE9-IE11 и Edge до Edge 14 поддерживают let, но ошибаетесь в вышеприведенном (они не создают новый i каждый раз, поэтому все функции, перечисленные выше, будут регистрировать 3 так, как они был бы, если бы мы использовали var). Край 14, наконец, понимает это правильно.

Попробуйте:

var funcs = [];
    
for (var i = 0; i < 3; i  ) {
    funcs[i] = (function(index) {
        return function() {
            console.log("My value: "   index);
        };
    }(i));
}

for (var j = 0; j < 3; j  ) {
    funcs[j]();
}

Edit (2014):

Лично я считаю, что более свежий ответ @ Aust {@} об использовании — лучший способ сделать это сейчас. Также есть lo-dash / underscore .bind, когда вам не нужно или вы хотите связываться с _.partial For-each над массивом в JavaScript? bindthisArg.

Я думал, что это было что-то вроде этого: Function.prototype.bind

var funcs = {};
for (var i = 0; i < 3; i  ) {
  funcs[i] = function(x) {
    console.log('My value: '   x);
  }.bind(this, i);
}
for (var j = 0; j < 3; j  ) {
  funcs[j]();
}

Где

— это мой массив, но это неверно.

function log(x) {
  console.log('My value: '   x);
}

var funcs = [];

for (var i = 0; i < 3; i  ) {
  funcs[i] = log.bind(this, i);
}

for (var j = 0; j < 3; j  ) {
  funcs[j]();
}

немедленного вызова функции TL ; DR Это отправляет итератор

for (var i = 0; i < 3; i  ) {

    (function(index) {

        console.log('iterator: '   index);
        //now you can also loop an ajax call here 
        //without losing track of the iterator value:   $.ajax({});
    
    })(i);

}

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

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

Итак, как уже упоминалось, проблема в том, что внутренняя функция ссылается на ту же переменную i. Так почему бы нам просто не создать новую локальную переменную каждая итерация, и вместо этого есть ссылка на внутреннюю функцию?

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML  = '<p>'   msg   '</p>';};

var funcs = {};
for (var i = 0; i < 3; i  ) {
    var ilocal = i; //create a new local variable
    funcs[i] = function() {
        console.log("My value: "   ilocal); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j  ) {
    funcs[j]();
}

Как и раньше, где каждая внутренняя функция выводила последнее значение, присвоенное i, теперь каждая внутренняя функция просто выводит последнее значение, присвоенное ilocal. Но не должно ли каждая итерация иметь свою собственную ilocal?

Оказывается, в этом проблема. Каждая итерация использует одну и ту же область видимости, поэтому каждая итерация после первой просто перезаписывает цикл ilocal. От MDN :

Важно: JavaScript не имеет области видимости блока. Переменные, введенные с блоком, попадают в область действия содержащей их функции или сценария, и последствия их установки сохраняются за пределами самого блока. Другими словами, операторы блока не вводят область действия. Хотя «автономные» блоки являются допустимым синтаксисом, вы не хотите использовать автономные блоки в JavaScript, потому что они не делают то, что вы думаете, они делают, если вы думаете, что они делают что-то вроде таких блоков в C или Java.

Повторено для акцента:

JavaScript не имеет блочной области видимости. Переменные, введенные с блоком, попадают в область действия содержащей их функции или сценария

Это можно увидеть, проверив ilocal перед тем, как объявить его в каждой итерации: {* } Именно поэтому этот баг такой хитрый. Даже если вы переделываете переменную, Javascript не выдаст ошибку, а JSLint даже не выдаст предупреждение. Вот почему лучший способ решить эту проблему — воспользоваться преимуществами замыканий, что, по сути, означает, что в Javascript внутренние функции имеют доступ к внешним переменным, поскольку внутренние области «заключают» внешние области.

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML  = '<p>'   msg   '</p>';};

var funcs = {};
for (var i = 0; i < 3; i  ) {
  console.log(ilocal);
  var ilocal = i;
}

, например

Closures

, другие итерируемые объекты (ES2015), коллекции DOM и так далее. ilocal, я быстро отмечу, что вы можете использовать опции ES2015 ilocal now

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML  = '<p>'   msg   '</p>';};

var funcs = {};
for (var i = 0; i < 3; i  ) {
    funcs[i] = (function() { //create a new scope using a wrapper function
        var ilocal = i; //capture i into a local var
        return function() { //return the inner function
            console.log("My value: "   ilocal);
        };
    })(); //remember to run the wrapper function
}
for (var j = 0; j < 3; j  ) {
    funcs[j]();
}

, даже на ядрах ES5, переменными ilocal транспилирование

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML  = '<p>'   msg   '</p>';};

var funcs = {};
for (var i = 0; i < 3; i  ) {
    funcs[i] = wrapper(i);
}
for (var j = 0; j < 3; j  ) {
    funcs[j]();
}
//creates a separate environment for the inner function
function wrapper(ilocal) {
    return function() { //return the inner function
        console.log("My value: "   ilocal);
    };
}

Где

ES2015 для ES5. Найдите ключевое слово «ES2015 transpiling» / «ES6 transpiling», чтобы узнать больше … let Хорошо, давайте посмотрим на наши параметры:

//overwrite console.log() so you can see the console output
console.log = function(msg) {document.body.innerHTML  = '<p>'   msg   '</p>';};

var funcs = {};
for (let i = 0; i < 3; i  ) { // use "let" to declare "i"
    funcs[i] = function() {
        console.log("My value: "   i); //each should reference its own local variable
    };
}
for (var j = 0; j < 3; j  ) { // we can use "var" here without issue
    funcs[j]();
}

Фактические массивы , на котором основана моя информация. У вас есть три варианта в

ECMAScript 5 let и const ECMAScript 2015 let («ES2015», «ES6»):

var funcs = [];

for (let i = 0; i < 3; i  ) {          
    funcs[i] = function() {            
      console.log("My value: "   i); 
    };
}

val будет затем указывать на объект, относящийся к конкретному повороту цикла, и будет возвращать правильное значение без дополнительной записи о замыкании. Это, очевидно, значительно упрощает эту проблему.

const аналогично let с дополнительным ограничением, что имя переменной не может быть привязано к новой ссылке после первоначального присвоения.

Теперь доступна поддержка браузеров для тех, кто ориентирован на последние версии браузеров. const / let, которые в настоящее время поддерживаются в последних версиях Firefox, Safari, Edge и Chrome. Он также поддерживается в Node, и вы можете использовать его где угодно, используя такие инструменты сборки, как Babel. Вы можете увидеть рабочий пример здесь: http://jsfiddle.net/ben336/rbU4t/2/

Документы здесь:

Однако остерегайтесь того, что IE9-IE11 и Edge до Edge 14 поддерживают let, но ошибаетесь в вышеприведенном (они не создают новый i каждый раз, поэтому все функции, перечисленные выше, будут регистрировать 3 так, как они был бы, если бы мы использовали var). Край 14, наконец, понимает это правильно.

Еще один способ сказать, что i в вашей функции связаны во время ее выполнения, а не во время ее создания.

Когда вы создаете замыкание, i является ссылкой на переменную, определенную во внешней области видимости, а не ее копией, как это было при создании замыкания. Он будет оценен во время исполнения.

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

Просто подумал, что я добавлю объяснение для ясности. Для решения лично я бы пошел с Харто, так как это самый очевидный способ сделать это из ответов здесь. Любой из опубликованного кода будет работать, но я бы предпочел фабрику замыканий, а не писать кучу комментариев, чтобы объяснить, почему я объявляю новую переменную (Фредди и 1800-е) или у меня странный встроенный синтаксис замыкания (apphacker).

Вам нужно понять, что область видимости переменных в javascript основана на функции. Это важное отличие, чем, скажем, c #, когда у вас есть область видимости блока, и просто копирование переменной в единицу внутри for будет работать.

Оборачивание в функцию, которая оценивает возвращение функции, как ответ apphacker, поможет, так как переменная теперь имеет область действия функции.

. Также вместо var есть ключевое слово let, которое позволит использовать правило области блока. В этом случае определение переменной внутри for сделает свое дело. Тем не менее, ключевое слово let не является практическим решением из-за совместимости.

var funcs = {};

for (var i = 0; i < 3; i  ) {
  let index = i; //add this
  funcs[i] = function() {
    console.log("My value: "   index); //change to the copy
  };
}

for (var j = 0; j < 3; j  ) {
  funcs[j]();
}

Вот еще один вариант метода, похожий на метод Бьорна (apphacker), который позволяет назначать значение переменной внутри функции, а не передавать ее в качестве параметра, что иногда может быть более понятным:

var funcs = [];
for (var i = 0; i < 3; i  ) {
    funcs[i] = (function() {
        var index = i;
        return function() {
            console.log("My value: "   index);
        }
    })();
}

. Обратите внимание, что независимо от используемой вами методики, переменная index становится своего рода статической переменная, привязанная к возвращенной копии внутренней функции. Т.е. изменения его значения сохраняются между вызовами. Это может быть очень удобно.

. Здесь описана распространенная ошибка с использованием замыканий в JavaScript.

Функция определяет новую среду

Подумайте:

function makeCounter()
{
  var obj = {counter: 0};
  return {
    inc: function(){obj.counter   ;},
    get: function(){return obj.counter;}
  };
}

counter1 = makeCounter();
counter2 = makeCounter();

counter1.inc();

alert(counter1.get()); // returns 1
alert(counter2.get()); // returns 0

За каждый раз, когда makeCounter вызвано, {counter: 0} приводит к созданию нового объекта. Также создается новая копия obj также создается для ссылки на новый объект. Таким образом, counter1 и counter2 не зависят друг от друга.

замыкания в циклах

Использовать замыкание в цикле сложно.

Подумайте:

var counters = [];

function makeCounters(num)
{
  for (var i = 0; i < num; i  )
  {
    var obj = {counter: 0};
    counters[i] = {
      inc: function(){obj.counter  ;},
      get: function(){return obj.counter;}
    }; 
  }
}

makeCounters(2);

counters[0].inc();

alert(counters[0].get()); // returns 1
alert(counters[1].get()); // returns 1

Обратите внимание, что counters[0] и counters[1], являются независимыми не . Фактически они работать с тем же obj!

Это потому, что существует только одна копия obj, совместно используемая на всех итерациях цикла, возможно, из соображений производительности. Даже если {counter: 0} создает новый объект в каждой итерации, одну и ту же копию obj будет просто обновлено с помощью Отношение к новейшему объекту.

Решение состоит в том, чтобы использовать другую вспомогательную функцию:

function makeHelper(obj)
{
  return {
    inc: function(){obj.counter  ;},
    get: function(){return obj.counter;}
  }; 
}

function makeCounters(num)
{
  for (var i = 0; i < num; i  )
  {
    var obj = {counter: 0};
    counters[i] = makeHelper(obj);
  }
}

Это работает, потому что локальные переменные в области действия функции напрямую, как а также переменные аргумента функции, при вводе выделяются новые копии.

Для подробного обсуждения, пожалуйста, смотрите ошибки закрытия JavaScript и их использование

Самое простое решение —

Вместо использования:

var funcs = [];
for(var i =0; i<3; i  ){
    funcs[i] = function(){
        alert(i);
    }
}

for(var j =0; j<3; j  ){
    funcs[j]();
}

, который предупреждает «2», 3 раза. Это связано с тем, что анонимные функции, созданные в цикле for, имеют одно и то же замыкание, и в этом замыкании значение i одинаково. Используйте это, чтобы предотвратить совместное закрытие:

var funcs = [];
for(var new_i =0; new_i<3; new_i  ){
    (function(i){
        funcs[i] = function(){
            alert(i);
        }
    })(new_i);
}

for(var j =0; j<3; j  ){
    funcs[j]();
}

. Идея этого заключается в том, чтобы инкапсулировать все тело цикла for с помощью цикла IIFE (выражение с немедленным вызовом функции) и передача new_i в качестве параметра и захват его как i. Поскольку анонимная функция выполняется немедленно, значение i различно для каждой функции, определенной внутри анонимной функции.

Такое решение подходит для любой такой проблемы. так как это потребует минимальных изменений в исходном коде, страдающем от этой проблемы. На самом деле, это по замыслу, это не должно быть проблемой вообще!

попробуйте этот более короткий

  • никакого массива

  • никакого дополнительного для работы цикла

for (var i = 0; i < 3; i  ) {
    createfunc(i)();
}

function createfunc(i) {
    return function(){console.log("My value: "   i);};
}

http://jsfiddle.net/7P6EN/

Основная проблема с кодом, показанным OP, заключается в том, что { *} никогда не читается до второго цикла. Для демонстрации представьте, что видите ошибку внутри кода i 3. Использование

funcs[i] = function() {            // and store them in funcs
    throw new Error("test");
    console.log("My value: "   i); // each should log its value.
};

на самом деле не возникает до тех пор, пока funcs[someIndex] Вы получите людей, говорящих вам, чтобы использовать(). Используя ту же логику, должно быть очевидно, что значение i также не собирается до этой точки. Когда исходный цикл завершается, i приносит i со значением 3, что приводит к тому, что условие i < 3 и заканчивается цикл. На этом этапе i, 3 и так, когда используется funcs[someIndex]() и i оценивается, это 3 — каждый раз.

Чтобы обойти это, вы должны оценить i, как он встречается. Обратите внимание, что это уже произошло в форме funcs[i]), но это {* } Другой вариант — создать объект функции, который сможет закрывать переменную. Это может быть достигнуто, таким образом,

Вот простое решение, которое использует

jsFiddle Demo

funcs[i] = new function() {   
    var closedVariable = i;
    return function(){
        console.log("My value: "   closedVariable); 
    };
};

(работает в IE9): forEach будет следовать этому порядку. (Подробности в

var funcs = [];
[0,1,2].forEach(function(i) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: "   i); // each should log its value.
    };
})
for (var j = 0; j < 3; j  ) {
    funcs[j]();                        // and now let's run each one to see
}

функциях JavaScript «закрывают» область, к которой они имеют доступ при объявлении, и сохраняют доступ к этой области, даже когда переменные в этой области изменяются.

My value: 0
My value: 1
My value: 2

Каждая функция в приведенном выше массиве закрывается над глобальной областью действия (глобальной, просто потому, что это та область, в которой они объявлены).

var funcs = []

for (var i = 0; i < 3; i  = 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k  = 1) {
  funcs[k]()
}

Позже эти функции вызываются, регистрируя самое последнее значение

в глобальной области видимости. Это магия и разочарование закрытия. i «Функции JavaScript закрывают область, в которой они объявлены, и сохраняют доступ к этой области, даже если значения переменных внутри этой области изменяются».

Использование

массивов let вывод будет: var массив for определяет область видимости переменных. Блоки обозначаются фигурными скобками, но в случае цикла for переменная инициализации,

var funcs = []

for (let i = 0; i < 3; i  = 1) {
  funcs[i] = function () {
    console.log(i)
  }
}

for (var k = 0; k < 3; k  = 1) {
  funcs[k]()
}

Обратите внимание, что для тех, кто использует новый let в нашем случае считается объявленной в скобках.) i После прочтения различных решений я хотел бы добавить, что причина, по которой эти решения работают, заключается в том, чтобы опираться на концепцию

цепочку областей видимости . Это способ, которым JavaScript разрешает переменную во время выполнения. Каждое определение функции формирует область видимости, состоящую из всех локальных переменных, объявленных

  • того, что у объекта есть свои var собственные arguments.
  • при выполнении функции среда выполнения оценивает переменные путем поиска
  • . Если переменная может быть найдена в определенной точке цепочки, она прекратит поиск и будет использовать ее, в противном случае она будет продолжаться до тех пор, пока не будет достигнута глобальная область действия, которая принадлежит . Это способ, которым JavaScript разрешает переменную во время выполнения. в спецификации window.

Когда

funcs = {};
for (var i = 0; i < 3; i  ) {         
  funcs[i] = function inner() {        // function inner's scope contains nothing
    console.log("My value: "   i);    
  };
}
console.log(window.i)                  // test value 'i', print 3

будет выполнено, цепочка областей действия будетfuncs выполняется, цепочка областей действия будет function inner -> global. Поскольку переменная i не может быть найдена в function inner (ни объявлена ​​с использованием var, ни передана в качестве аргументов), она продолжает поиск до значения i В конечном итоге находится в глобальной области видимости window.i.

, что мой предыдущий тест был не совсем верным.) Конечно, вы не сделаете этого во встроенном коде. Вы бы написали служебную функцию. Возможно: 4. Используйте сделал: ES2015 добавляет, что

funcs = {};
function outer(i) {              // function outer's scope contains 'i'
  return function inner() {      // function inner, closure created
   console.log("My value: "   i);
  };
}
for (var i = 0; i < 3; i  ) {
  funcs[i] = outer(i);
}
console.log(window.i)          // print 3 still

будет выполнено, цепочка областей действия будетfuncs итераторами function inner -> function outer в JavaScript. Самым простым способом использования итераторов является то, что новый i. Это выглядит так: i Под прикрытием, которое получает window.i Более подробная информация может быть найдена

из массива и проходит по нему, получая значения из него. Это не проблема, связанная с использованием говорится, что
имеет, потому что он использует итератор, определенный объектом (массивом), а массивы определяют, что их итераторы выполняют итерацию через их

entry

var funcs = [];
for (let i = 0; i < 3; i  ) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: "   i); // each should log its value.
    };
}
for (let j = 0; j < 3; j  ) {
    funcs[j]();                        // and now let's run each one to see
}

(не их свойства). В отличие от let вывод будет: var.

Иногда вам может понадобиться использовать итератор

const funcs = [1, 2, 3].map(i => () => console.log(i));
funcs.map(fn => fn())
const buttons = document.getElementsByTagName("button");
Object
  .keys(buttons)
  .map(i => buttons[i].addEventListener('click', () => console.log(i)));
<button>0</button><br>
<button>1</button><br>
<button>2</button>

явно forEach по этой причине. for(var i ...). Вы также можете сделать это, хотя это намного более неудобно, чем

[0,2,3].forEach(function(i){ console.log('My value:', i); });
// My value: 0
// My value: 2
// My value: 3

. Это выглядит так: forEach Итератор — это объект, соответствующий определению Итератора в спецификации. Его

возвращает новый

var funcs = [];
for (var i = 0; i < 3; i  ) {          // let's create 3 functions
    funcs[i] = function() {            // and store them in funcs
        console.log("My value: "   i); // each should log its value.
    };
}
for (var j = 0; j < 3; j  ) {
    funcs[j]();                        // and now let's run each one to see
}

результирующего объекта funcs[] каждый раз, когда вы вызываете его. У результирующего объекта есть свойство, i сообщает нам, сделано ли это, и массив свойства funcs становится равным 3, поэтому func с значение для этой итерации. (i = 3, является необязательным, если вызывается funcs[j](), которая уже была увеличена до 3. i Теперь, чтобы решить эту проблему, мы Есть много вариантов. Ниже приведены два из них:

Значение

  1. , варьируется в зависимости от того, какие массивы итераторов ; поддерживают (как минимум) три функции, которые возвращают итераторы: i. Это как окно, которое вы можете открыть с помощью let — это запись массива для этой итерации (index. Это как окно, которое вы можете открыть с помощью let. Поэтому, когда вызов выполняется, i будет использоваться, и его область действия завершится после инициализации. И для вызова index в предыдущем примере). index: Возвращает итератор, где каждый {* } Другим вариантом может быть введение

    var funcs = [];
    for (var i = 0; i < 3; i  ) {          
        let index = i;
        funcs[i] = function() {            
            console.log("My value: "   index); 
        };
    }
    for (var j = 0; j < 3; j  ) {
        funcs[j]();                        
    }
    
  2. , которая возвращает реальную функцию: tempFunc. Мы проверим, что на самом деле происходит, когда вы объявить

    var funcs = [];
    function tempFunc(i){
        return function(){
            console.log("My value: "   i);
        };
    }
    for (var i = 0; i < 3; i  ) {  
        funcs[i] = tempFunc(i);                                     
    }
    for (var j = 0; j < 3; j  ) {
        funcs[j]();                        
    }
    

один за другим. var и letпо одному.

Case1 : массивов var

<script>
   var funcs = [];
   for (var i = 0; i < 3; i  ) {
     funcs[i] = function () {
        debugger;
        console.log("My value: "   i);
     };
   }
   console.log(funcs);
</script>

Теперь откройте окно консоли Chrome , нажав F12 и обновляют страницу. Расширьте каждые 3 функции внутри массива. Вы увидите свойство [[Scopes]]. Разверните это. Вы увидите один объект массива с именем "Global", разверните его. Вы найдете свойство 'i' объявленное в объекте со значением 3.

enter image description here

enter image description here

Заключение:

  1. Когда вы объявляете переменную с помощью 'var' вне функции, она становится глобальной переменной (вы можете проверить, набрав i или window.i в окне консоли. Это вернет 3).
  2. объявленная вами анонимная функция не будет вызывать и проверять значение внутри функции, пока вы не вызовете функции.
  3. Когда вы вызываете функцию, console.log("My value: " i) Берет значение из его Global object и отображает результат.

CASE2 : using let

Теперь замените 'var'. Это как окно, которое вы можете открыть с помощью 'let'

<script>
    var funcs = [];
    for (let i = 0; i < 3; i  ) {
        funcs[i] = function () {
           debugger;
           console.log("My value: "   i);
        };
    }
    console.log(funcs);
</script>

. Сделайте то же самое, Перейдите к областям действия. Теперь вы увидите два объекта "Block" и "Global". Теперь разверните Block объект, вы увидите, что здесь определено «i», и странно то, что для каждой функции значение if i отличается (0, 1, 2).

enter image description here

Заключение:

когда вы объявляете переменную с помощью цикла 'let' даже вне функции, но внутри цикла, эта переменная не будет глобальной переменной, она станет Block, которая доступна только для одной и той же функции. По этой причине мы получаем значение i для каждой функции, когда мы вызываем функции.

Для получения более подробной информации о том, как работает ближе, ознакомьтесь с удивительным видеоуроком https://youtu.be/71AtaJpJHw0

Причина, по которой ваш оригинальный пример не сработал, заключается в том, что все замыкания, созданные вами в цикле, ссылаются на один и тот же кадр. Фактически наличие трех методов для одного объекта с единственной переменной i. Все они распечатаны одинакового значения.

Используйте Закрытие структура, это уменьшит ваш дополнительный цикл for. Вы можете сделать это в одном цикле for:

var funcs = [];
for (var i = 0; i < 3; i  ) {     
  (funcs[i] = function() {         
    console.log("My value: "   i); 
  })(i);
}

, который я предпочитаю использовать forEach, которая имеет свое собственное закрытие с созданием псевдодальности:

var funcs = [];

new Array(3).fill(0).forEach(function (_, i) { // creating a range
    funcs[i] = function() {            
        // now i is safely incapsulated 
        console.log("My value: "   i);
    };
});

for (var j = 0; j < 3; j  ) {
    funcs[j](); // 0, 1, 2
}

Это выглядит страшнее, чем диапазоны в других языках, но, на мой взгляд, менее чудовищно, чем другие решения.

var funcs = [];
for (var i = 0; i < 3; i  ) {      // let's create 3 functions
  funcs[i] = function(param) {          // and store them in funcs
    console.log("My value: "   param); // each should log its value.
  };
}
for (var j = 0; j < 3; j  ) {
  funcs[j](j);                      // and now let's run each one to see with j
}

. Вы можете использовать декларативный модуль для списков данных, такой как query-js (*). В этих ситуациях я лично нахожу декларативный подход менее удивительным

var funcs = Query.range(0,3).each(function(i){
     return  function() {
        console.log("My value: "   i);
    };
});

Затем вы можете использовать свой второй цикл и получить ожидаемый результат, или вы можете сделать

funcs.iterate(function(f){ f(); });

(*). Я автор query-js и поэтому склонен к его использованию, так что не принимайте мои слова в качестве рекомендации для сказанного. библиотека только для декларативного подхода :)

Многие решения кажутся правильными, но они не упоминают, что он называется Currying Смотрите

var funcs = [];
for (var i = 0; i < 3; i  ) {      // let's create 3 functions
  funcs[i] = curryShowValue(i);
}
for (var j = 0; j < 3; j  ) {
  funcs[j]();                      // and now let's run each one to see
}

function curryShowValue(i) {
  return function showValue() {
    console.log("My value: "   i);
  }
}

ниже. В частности, обратите внимание, что это не удастся в IE8 и более ранних версиях, которые не позволяют использовать предоставленные хостом объекты как , как это. .

распространяет синтаксис (

Create variable `funcs` and assign it an empty array;  
Loop from 0 up until it is less than 3 and assign it to variable `i`;
    Push to variable `funcs` next function:  
        // Only push (save), but don't execute
        **Write to console current value of variable `i`;**

// First loop has ended, i = 3;

Loop from 0 up until it is less than 3 and assign it to variable `j`;
    Call `j`-th function from variable `funcs`:  
        **Write to console current value of variable `i`;**  
        // Ask yourself NOW! What is the value of i?

при вызове функции? Потому что первый цикл создан с условием i Также возможно использовать i < 3 распространяет синтаксис i = 3.

в истинный массив, с синтаксисом распространения это становится довольно лаконичным: i Итак, ваша цель — сначала сохранить значение

в функции и только после этого сохранить функции к i (MDN) funcs Таким образом, каждая функция будет иметь свою собственную переменную

var funcs = [];
for (var i = 0; i < 3; i  ) {          // let's create 3 functions
    funcs[i] = function(x) {            // and store them in funcs
        console.log("My value: "   x); // each should log its value.
    }.bind(null, i);
}
for (var j = 0; j < 3; j  ) {
    funcs[j]();                        // and now let's run each one to see
}

и мы устанавливаем это x в каждой итерации. x со значением i Это только один из нескольких способов решения этой проблемы.

предоставленный хостом

к функции возврата. this При связывании

var funcs = [];

function createFunc(i) {
  return function() {
    console.log('My value: '   i); //log value of i.
  }.call(this);
}

for (var i = 1; i <= 5; i  ) {  //5 functions
  funcs[i] = createFunc(i);     // call createFunc() i=5 times
}

(сейчас), но это важно проверить. Причина в том, что большая часть this Используйте let (block-scope) вместо var.

§

var funcs = [];
for (let i = 0; i < 3; i  ) {      
  funcs[i] = function() {          
    console.log("My value: "   i); 
  };
}
for (var j = 0; j < 3; j  ) {
  funcs[j]();                      
}

, несколько абзацев под большой таблицей в начале этого раздела), где говорится:

var funcs = [];
// let's create 3 functions
for (var i = 0; i < 3; i  ) {
  // and store them in funcs
  funcs[i] = function() {
    // each should log its value.
    console.log("My value: "   i);
  };
}
for (var j = 0; j < 3; j  ) {
  // and now let's run each one to see
  funcs[j]();
}

Мое значение: 3

В то время как я хотел бы, чтобы выход:
В то время как я хотел бы, чтобы выход:
В то время как я хотел бы, чтобы выход:

Принимая во внимание, что я хотел бы вывести:

Мое значение: 0
Мое значение: 1
Мое значение: 2


Та же проблема возникает, когда вызвана задержка в запуске функции с помощью прослушивателей событий:

var buttons = document.getElementsByTagName("button");
// let's create 3 functions
for (var i = 0; i < buttons.length; i  ) {
  // as event listeners
  buttons[i].addEventListener("click", function() {
    // each should log its value.
    console.log("My value: "   i);
  });
}
<button>0</button>
<br />
<button>1</button>
<br />
<button>2</button>

… или асинхронного кода, например используя обещания:

// Some async wait function
const wait = (ms) => new Promise((resolve, reject) => setTimeout(resolve, ms));

for (var i = 0; i < 3; i  ) {
  // Log `i` as soon as each promise resolves.
  wait(i * 100).then(() => console.log(i));
}

Каково решение этой основной проблемы?

для конкретного хост-объекта действительно выбирает и сохраняет значения свойств, но

после тайм-аута по завершении будет напечатано 2 для обоих. Это связано с тем, что функция обратного вызова обращается к значению, основанному на лексической области

// ****************************
// COUNTER BEING A PRIMITIVE
// ****************************
function test1() {
    for (var i=0; i<2; i  ) {
        setTimeout(function() {
            console.log(i);
        });
    }
}
test1();
// 2
// 2

, всегда генерирует там, где была определена функция. Чтобы передать и сохранить значение во время определения обратного вызова, мы можем создать

сохранялись общие объекты, похожие на массивы, чтобы сохранить значение до вызова. Это можно сделать следующим образом: Закрытие, чтобы сохранить значение до вызова обратного вызова. Это можно сделать следующим образом:

function test2() {
    function sendRequest(i) {
        setTimeout(function() {
            console.log(i);
        });
    }

    for (var i = 0; i < 2; i  ) {
        sendRequest(i);
    }
}
test2();
// 1
// 2

Что особенного в этом заключается в том, что «Примитивы передаются по значению и копируются. Таким образом, когда определено замыкание, они сохраняют значение из предыдущего цикла. »

COUNTER BEING OF OBJECT

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

// ****************************
// COUNTER BEING AN OBJECT
// ****************************
function test3() {
    var index = { i: 0 };
    for (index.i=0; index.i<2; index.i  ) {
        setTimeout(function() {
            console.log('test3: '   index.i);
        });
    }
}
test3();
// 2
// 2

Таким образом, даже если для переменной, переданной в качестве объекта, создано замыкание, значение индекса цикла не будет сохранено. Это показывает, что значения объекта не копируются, а доступ к ним осуществляется через ссылку.

function test4() {
    var index = { i: 0 };
    function sendRequest(index, i) {
        setTimeout(function() {
            console.log('index: '   index);
            console.log('i: '   i);
            console.log(index[i]);
        });
    }

    for (index.i=0; index.i<2; index.i  ) {
        sendRequest(index, index.i);
    }
}
test4();
// index: { i: 2}
// 0
// undefined

// index: { i: 2}
// 1
// undefined

Эта проблема часто встречается в асинхронном коде, переменная i является изменчивой, и во время вызова функции код выполняет с помощью i, и i, будут видоизменены до своего последнего значения, что означает, что все функции, созданные в цикле, создадут Закрытие и i будет равно 3 (верхняя граница 1 цикла for.

Обходной путь — создать функция, которая будет хранить значение i для каждой итерации и заставлять копию i (поскольку это примитив, думайте о нем как о снимке, если он вам поможет).

Просто замените ключевое слово var на let.

var — это область действия функции.

let — это область видимости блока.

Когда вы начинаете Код цикла for будет повторяться и присваивать значению i значение 3, которое в вашем коде останется 3. Я предлагаю вам прочитать больше о областях в узле (let, var, const и других)

funcs = [];
for (let i = 0; i < 3; i  ) {      // let's create 3 functions
  funcs[i] =async function() {          // and store them in funcs
    await console.log("My value: "   i); // each should log its value.
  };
}
for (var j = 0; j < 3; j  ) {
  funcs[j]();                      // and now let's run each one to see
}

Хотя этот вопрос старый и на него дан ответ, у меня есть еще одно довольно интересное решение:

var funcs = [];

for (var i = 0; i < 3; i  ) {     
  funcs[i] = function() {          
    console.log("My value: "   i); 
 };
}

for (var i = 0; i < 3; i  ) {
  funcs[i]();
}

Изменение настолько мало, что почти трудно понять, что я сделал. Я переключил второй итератор с aj на i. каким-то образом обновляет состояние i, чтобы дать вам желаемый результат. Я сделал это случайно, но это имеет смысл, учитывая предыдущие ответы.

Я написал это, чтобы указать на это небольшое, но очень важное отличие. Надеюсь, это поможет прояснить ситуацию для других учеников, таких как я.

Примечание: я не разделяю это, потому что я думаю, что это правильный ответ. Это ошибочное решение, которое, вероятно, сломается при определенных обстоятельствах. На самом деле, я очень удивлен, что это действительно работает.

Допустим, вы не используете es6 ; Вы можете использовать функцию IFFY:

var funcs = [];
for (var i = 0; i < 13; i  ) {      
funcs[i] = (function(x) {
console.log("My value: "   i)})(i);}

— цикл может быть лучшей идеей.

Однако , вы должны использовать соответствующие меры безопасности, чтобы гарантировать, что только желаемые свойства массива (то есть элементы массива) выполняются, поскольку у Суть в том, что команда LET хороша, но используется только сейчас. ОДНАКО команда LET на самом деле является просто комбинацией TRY-CATCH. Это работает вплоть до IE3 (я считаю). Использование комбинации TRY-CATCH — жизнь проста и хороша. Вероятно, почему люди EMCScript решили использовать его. Также не требуется функция setTimeout (). Так что время не потеряно. По сути, вам нужна одна комбинация TRY-CATCH для цикла FOR. Вот пример:

— цикл также будет перечисляться в устаревших браузерах, или если дополнительные свойства определены как

    for( var i in myArray ){
       try{ throw i }
       catch(ii){
// Do whatever it is you want to do with ii
          }
       }

Это доказывает, насколько ужасен javascript в отношении того, как работает «замыкание» и «не замыкание».

В случае:

ECMAScript 5

var funcs = [];

for (var i = 0; i < 3; i  ) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: "   i); // each should log its value.
  };
}

В случае

из-за этого скрученного замыкания замыкания javascript, ‘console.log) («Мое значение:» i) ; ‘печатает i из внешней функции’ createfunc (i) ‘

var funcs = [];

function createfunc(i) {
    return function() { console.log("My value: "   i); };
}

for (var i = 0; i < 3; i  ) {
    funcs[i] = createfunc(i);
}

все потому, что javascript не может спроектировать что-то приличное, например, «статическую» переменную внутри функции, как это делает язык программирования C!

При поддержке

  asyncIterable = [1,2,3,4,5,6,7,8];

  (async function() {
       for await (let num of asyncIterable) {
         console.log(num);
       }
    })();

, вы можете использовать ключевые слова ES6 для этого точного обстоятельства. Итак, переменная let и const get var EDIT: hoisted обновляется для всех i …, мы можем просто использовать closures до ES5, эта проблема может быть решена только с помощью let («ES2015», «ES6»):

var funcs = [];
for (let i = 0; i < 3; i  ) {          
    funcs[i] = function() {            
      console.log("My value: "   i); 
    };
}

for loop, заслуживает упоминания здесь: Закрытие.

var to let в первую очередь для цикла решит проблему. Почему бы просто не вызывать каждую функцию в первом (и единственном) цикле сразу после их создания, например:

var funcs = [];
for (let i = 0; i < 3; i  ) {      // let's create 3 functions
  funcs[i] = function() {          // and store them in funcs
    console.log("My value: "   i); // each should log its value.
  };
}
for (var j = 0; j < 3; j  ) {
  funcs[j]();                      // and now let's run each one to see
}

Давайте воспользуемся преимуществом

 var funcs = [];
    for (var i = 0; i < 3; i  ) {
    // let's create 3 functions
    funcs[i] = function() {
    // and store them in funcs
    console.log("My value: "   i); // each should log its value.
    };
    funcs[i]();// and now let's run each one to see
    }

перестает быть переменной new Function, изменяющей массив i во время цикла, по индексу Закрытие и становится только частью текста:

var funcs = [];
for (var i = 0; i < 3; i  ) {
    var functionBody = 'console.log("My value: '   i   '");';
    funcs[i] = new Function(functionBody);
}

for (var j = 0; j < 3; j  ) {
    funcs[j]();
}

Уже много правильных ответов на этот вопрос. Не многие используют функциональный подход, хотя. Вот альтернативное решение, использующее forEach, который хорошо работает с обратными вызовами и замыканиями:

let arr = [1,2,3];

let myFunc = (val, index) => {
console.log('val: ' val 'nindex: ' index);
};

arr.forEach(myFunc);

Понравилась статья? Поделиться с друзьями:
JavaScript & TypeScript
Adblock
detector