2015-11-13 2 views
5

Можно было бы распечатать a, b, c.Почему этот простой цикл не работает так, как ожидалось?

var i, rowName; 
for (i = 0; i < 3; i++, rowName = ['a', 'b', 'c'][i]) { 
    console.log(rowName); 
} 

Однако вместо этого, он печатает undefined, b, c. Зачем?

Чтобы уточнить: я знаю, как сделать эту работу; то, что мне интересно, это , почему выше не работает.

+3

Окончательное выражение оценивается только на * конце * каждой итерации цикла. «rowName» до этого не определено. См. [For @ MDN] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for) – showdev

+1

Третье (и второе) выражение в цикле 'for' не является оценивается до конца каждой итерации. Если бы он был оценен заранее, чтобы назначить 'rowName', он также сразу увеличит' i' до '1', пропустив начальное значение' 0'. –

+0

Документация вряд ли может быть более четкой. В нем указано «окончательное выражение Выражение, которое должно быть оценено ** в конце каждой итерации цикла **. Это происходит до следующей оценки условия. Обычно используется для обновления или увеличения переменной счетчика». –

ответ

5

Причина печатает undefined, b, c из-за того, как for loop работ.

for (initialization; condition; final expression) 

Давайте разложим ваш цикл.

инициализации: i = 0

состояние: i < 3

окончательное выражение: i++, rowName = ['a', 'b', 'c'][i]

Когда цикл первого ввода i устанавливается в 0. Это шаг инициализации. Затем проверяется состояние , i < 3. Это делается перед каждой итерацией, чтобы решить, продолжать ли цикл. После каждого цикла вычисляется окончательное выражение . В вашем примере вы увеличиваете i до установки rowName, равного элементу в ['a', 'b', 'c'] на основе текущего индекса.

В вашем случае, на первой итерации, rowName является undefined, потому что окончательное выражение еще предстоит оценить. Каждая итерация после этого ведет себя так, как вы ожидали бы, поскольку окончательное выражение уже было предварительно оценено.

+0

« В вашем примере вы увеличиваете 'i' перед установкой' rowName'. Правда, но что добавляет 'i' к присвоению' rowName'? Ложная. – PHPglue

+0

@PHPglue Как это вводит в заблуждение? Я не сказал, что это имеет какое-то отношение к другому.Однако оба утверждения представляют собой «окончательные выражения» или «операции после цикла» и, следовательно, оба они оцениваются после каждого цикла. – royhowie

+0

Это может смутить ОП в мысли, что имеет значение. – PHPglue

0

Я думаю, вы хотите этого?

var i, rowName; 
for (i = 0; i < 3; i++){ 
    rowName = ['a', 'b', 'c'][i]; 
    console.log(rowName); 
} 
+2

Ну конечно, но * почему * не работает мой первый пример? – Mark

+0

Поскольку присвоение 'rowname' происходит после каждого выполнения цикла. Итак, в первый раз, «rowname» не был назначен, поэтому вы получите «undefined». (Вы можете перенести присвоение в «среднее» выражение в заголовке цикла 'for'.) – dave

0

Вы переназначаете rowName на каждом шаге цикла, и для начала это undefined. Вот что вы можете сделать:

for(var i=0,rowName=['a','b','c'],l=rowName.length; i<l; i++){ 
    console.log(rowName[i]); 
} 

или что-то вроде:

var rowName = ['a', 'b', 'c']; 
for(var i=0,l=rowName.length; i<l; i++){ 
    console.log(rowName[i]); 
} 

Реальная проблема заключается в том, что третье условие внутри for(assign; test; execute) не не execute до, пока один test из цикла не будет выполнено. Если test не удался execute, это никогда не произойдет. Если test проходит execute, то действительно начинается после первого прохода цикла.

+0

Фрагмент OP пытается регистрировать каждое значение по отдельности. Хотя это будет регистрировать массив, в целом, каждую итерацию. –

2

Если вы хотите сделать хитрую одну линию стиля петли, «правильный» синтаксис:

var array = ['a', 'b', 'c']; 
for (var i = 0, rowName; rowName = array[ i++ ];) { 
    console.log(rowName); 
} 

Обратите внимание на окончание ; декларации в for цикла. Технически существует пустое заявление после ;, где вы обычно делаете i++.

В этом случае «условие» цикла for использует оператор присваивания Javascript. Если у вас есть такой код:

var a; 
if(a = 1) { // Note this is assignment = not comparison == 
    console.log('true'); 
} 

Это будет журнал «правда». Зачем? Потому что внутри выражения a = 1 фактически возвращает 1. И 1 является «правдивым», что означает, что он оценивается как истинный в булевом контексте, таком как оператор if.

Обратное также верно, если значение falsey:

var a; 
if(a = 0) { 
    console.log('true'); 
} 

Это не журнал, потому что a = 0 возвращается 0 (а также назначая от 0 до a). И 0 ложно.

Этот эксцентричный для цикла синтаксис только для определенных условий:

  • Если какой-либо из элементов массива являются «falsey» (null, undefined, "" и т.д.), она будет преждевременно завершает цикл, из-за как работают операторы, как указано выше.
  • Это предполагает, что вам не нужен индекс цикла i. Он будет отключен на 1 для каждой итерации цикла, потому что i++ выполнен доfor блок. То есть при первом выполнении вашего тела fori будет 1, а не объявленным начальным значением 0.
  • Единственное преимущество этого шаблона заключается в сохранении нескольких байтов. Обычно это не используется в реальном мире из-за вышеупомянутых двух подводных камней.