for

for

for 문은 괄호로 둘러싸고 세미콜론으로 구분한 세 개의 선택적 표현식으로 구성된 루프를 만들고, 뒤이어 루프에서 실행할 문(보통 블록 문)이 옵니다.

출처: for

본문

for 문은 괄호로 둘러싸고 세미콜론으로 구분한 세 개의 선택적 표현식(initialization, condition, afterthought)으로 구성된 루프를 만들고, 뒤이어 루프에서 실행할 문(보통 블록 문)이 옵니다.

구문 (Syntax)

for (initialization; condition; afterthought)
  statement
  • initialization: 루프가 시작되기 전에 한 번 평가되는 표현식(할당 표현식 포함) 또는 변수 선언입니다. 보통 카운터 변수를 초기화하는 데 사용됩니다. var 또는 let 키워드로 새 변수를 선택적으로 선언할 수 있습니다. var로 선언된 변수는 루프에 국한되지 않아 for 루프와 같은 스코프에 있습니다. let으로 선언된 변수는 문에 국한됩니다. 이 표현식의 결과는 버려집니다.
  • condition: 각 루프 반복 전에 평가되는 표현식입니다. 이 표현식이 true로 평가되면 statement가 실행되고, false로 평가되면 실행이 루프를 빠져나가 for 구조 다음의 첫 번째 문으로 갑니다. 이 조건 테스트는 선택적이며, 생략하면 조건이 항상 true로 평가됩니다.
  • afterthought: 각 루프 반복의 끝에서 평가되는 표현식입니다. condition의 다음 평가 전에 발생합니다. 일반적으로 카운터 변수를 갱신하거나 증가시키는 데 사용됩니다.
  • statement: 조건이 true로 평가되는 동안 실행되는 문입니다. 여러 문을 실행하려면 블록 문을 사용하세요. 루프 내에서 아무 문도 실행하지 않으려면 빈 문(;)을 사용하세요.

설명 (Description)

다른 루핑 문과 마찬가지로, statement 내부에서 제어 흐름 문을 사용할 수 있습니다.

  • break는 문 실행을 중단하고 루프 다음의 첫 번째 문으로 갑니다.
  • continue는 문 실행을 중단하고 afterthought를 재평가한 다음 condition을 재평가합니다.

예제: for 사용하기

다음 for 문은 변수 i를 선언하고 0으로 초기화하는 것으로 시작합니다. i가 9보다 작은지 확인하고, 두 개의 후속 문을 실행하며, 루프를 한 번 통과할 때마다 i를 1씩 증가시킵니다.

for (let i = 0; i < 9; i++) {
  console.log(i);
  // more statements
}

초기화 블록 구문

초기화 블록은 표현식과 변수 선언을 모두 허용합니다. 그러나 표현식은 괄호 없이 in 연산자를 사용할 수 없는데, 이는 for...in 루프와 모호하기 때문입니다.

for (let i = "start" in window ? window.start : 0; i < 9; i++) {
  console.log(i);
}
// SyntaxError: 'for-in' loop variable declaration may not have an initializer.

괄호를 사용해 해결할 수 있습니다.

// Parenthesize the whole initializer
for (let i = ("start" in window ? window.start : 0); i < 9; i++) {
  console.log(i);
}

선택적 for 표현식

for 루프 머리의 세 표현식 모두 선택적입니다. 예를 들어 초기화 블록을 사용해 변수를 초기화할 필요는 없습니다.

let i = 0;
for (; i < 9; i++) {
  console.log(i);
}

조건 부분도 선택적입니다. 이 표현식을 생략하면 무한 루프를 만들지 않도록 본문에서 반드시 루프를 break해야 합니다.

for (let i = 0; ; i++) {
  console.log(i);
  if (i > 3) break;
  // more statements
}

세 표현식을 모두 생략할 수도 있습니다. 다시 한번 루프를 끝내기 위해 break 문을 사용하고, break 문의 조건이 어떤 시점에서 true가 되도록 변수를 수정(증가)해야 합니다.

let i = 0;

for (;;) {
  if (i > 3) break;
  console.log(i);
  i++;
}

그러나 세 표현식 위치를 모두 완전히 사용하지 않는 경우(특히 첫 번째 표현식으로 변수를 선언하지 않고 상위 스코프의 무언가를 변경하는 경우)에는 while 루프를 사용하는 것을 고려하세요. 의도가 더 명확해집니다.

초기화 블록의 어휘적 선언

초기화 블록에서 변수를 선언하는 것은 상위 스코프에서 선언하는 것과 중요한 차이가 있습니다. 특히 루프 본문 내에서 클로저를 만들 때 그렇습니다.

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

이 코드는 예상대로 0, 1, 2를 로그합니다. 그러나 변수가 상위 스코프에 정의되어 있으면:

let i = 0;
for (; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}

3, 3, 3을 로그합니다. 그 이유는 각 setTimeouti 변수를 닫는 새 클로저를 만들지만, i가 루프 본문에 스코프되지 않으면 모든 클로저가 결국 호출될 때 같은 변수를 참조하게 되기 때문입니다. setTimeout()의 비동기 특성상 호출은 루프가 이미 종료된 후에 일어나므로, 대기 중인 모든 콜백 본문의 i 값이 3이 됩니다.

var 문을 초기화로 사용해도 같은 일이 발생합니다. var로 선언된 변수는 함수 스코프만 있고 어휘적 스코프가 없기(즉 루프 본문에 스코프될 수 없기) 때문입니다.

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000);
}
// Logs 3, 3, 3

초기화 블록의 스코프 효과는 선언이 루프 본문 내에서 발생하되, 마침 conditionafterthought 부분에서 접근 가능한 것처럼 이해할 수 있습니다. 더 정확히 말하면, let 선언은 for 루프에 의해 특별 처리됩니다. 초기화가 let 선언이면 매번 루프 본문이 평가된 후 다음이 발생합니다.

  • let 선언 변수를 가진 새 어휘 스코프가 생성됩니다.
  • 마지막 반복의 바인딩 값이 새 변수를 다시 초기화하는 데 사용됩니다.
  • afterthought가 새 스코프에서 평가됩니다.

따라서 afterthought 내에서 새 변수를 재할당해도 이전 반복의 바인딩에는 영향을 주지 않습니다.

본문 없는 for 사용하기

다음 for 주기는 afterthought 섹션에서 노드의 오프셋 위치를 계산하므로 문 섹션이 필요하지 않고, 대신 세미콜론이 사용됩니다.

function showOffsetPos(id) {
  let left = 0;
  let top = 0;
  for (
    let itNode = document.getElementById(id); // initialization
    itNode; // condition
    left += itNode.offsetLeft,
      top += itNode.offsetTop,
      itNode = itNode.offsetParent // afterthought
  ); // semicolon

  console.log(
    `Offset position of "${id}" element:
left: ${left}px;
top: ${top}px;`,
  );
}

showOffsetPos("content");

for 문 뒤의 세미콜론은 필수입니다. 빈 문(empty statement)으로 서 있기 때문입니다. 그렇지 않으면 for 문이 다음 console.log 줄을 자신의 문 섹션으로 삼아 로그가 여러 번 실행됩니다.

두 개의 반복 변수를 가진 for 사용하기

쉼표 연산자(comma operator)를 사용해 for 루프에서 동시에 갱신되는 두 개의 카운터를 만들 수 있습니다. 여러 letvar 선언도 쉼표로 결합할 수 있습니다.

const arr = [1, 2, 3, 4, 5, 6];
for (let l = 0, r = arr.length - 1; l < r; l++, r--) {
  console.log(arr[l], arr[r]);
}
// 1 6
// 2 5
// 3 4

더 알아보기