Закрытие JavaScript внутри циклов - простой практический пример

Независимо от того, где находится рабочий каталог. Это должно быть под вашим проектом Dir. Итак, мое решение

wd, _ := os.Getwd()
for !strings.HasSuffix(wd, "<yourProjectDirName>") {
    wd = filepath.Dir(wd)
}

raw, err := ioutil.ReadFile(fmt.Sprintf("%s/src/conf/conf.dev.json", wd))

Ваш путь должен всегда начинаться с вашего проекта Dir. Каждый раз, когда вы читаете файл в пакете и получаете доступ по адресу main.go или к вашему другому модулю пакета. Он всегда будет работать.

2648
задан LogicalBranch 23 May 2019 в 15:56
поделиться

5 ответов

Ну, проблема в том, что переменная 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 ++) {// и теперь давайте запустим каждый из них, чтобы увидеть 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 , будет его собственным замыканием. Параметр, передаваемый этому обработчику, является элементом массива, специфичным для данного конкретного шага итерации. Если он используется в асинхронном обратном вызове, он выиграет t сталкиваются с любыми другими обратными вызовами, установленными на других этапах итерации.

Если вы работаете в 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 поддерживают , допускают , но ошибаются в вышеприведенном (они не создают новый i каждый раз, поэтому все вышеперечисленные функции будут регистрировать 3 как они были бы, если бы мы использовали var ). Край 14, наконец, понимает это правильно.

2043
ответ дан 14 September 2019 в 05:59
поделиться

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

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

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

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

85
ответ дан 14 September 2019 в 05:59
поделиться

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

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

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

 var funcs = {}; for (var i = 0; i <3; i ++) {пусть index = i; // добавить это funcs [i] = function () {console.log ("My value:" + index); // перейти к копии}; } for (var j = 0; j <3; j ++) {funcs [j] (); } 
70
ответ дан 14 September 2019 в 05:59
поделиться

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

8
ответ дан 14 September 2019 в 05:59
поделиться

Попробуйте:

 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] (); } 

Редактировать (2014):

Лично я думаю, что недавний ответ @ Aust об использовании .bind - лучший способ сделать это сейчас. Также есть _. Частичное lo-dash / underscore, когда вам не нужно или не хотите связываться с bind thisArg .

.
367
ответ дан 14 September 2019 в 05:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: