for...in

for...in

for...in 문은 객체의 모든 열거 가능한(enumerable) 문자열 속성(symbol로 키가 지정된 속성은 무시)을 반복하며, 상속된 열거 가능한 속성도 포함합니다.

출처: for...in

본문

for...in 문은 객체의 모든 열거 가능한 문자열 속성을 반복하며, symbol로 키가 지정된 속성을 무시하고 상속된 열거 가능한 속성도 포함합니다.

구문 (Syntax)

for (variable in object)
  statement
  • variable: 각 반복에서 문자열 속성 이름을 받습니다. const, let, var로 선언하거나 할당 대상(예: 이전에 선언된 변수, 객체 속성, 구조 분해 패턴)이 될 수 있습니다. var로 선언된 변수는 루프에 국한되지 않아 for...in 루프와 같은 스코프에 있습니다.
  • object: symbol이 아닌 열거 가능한 속성이 반복되는 객체입니다.
  • statement: 모든 반복에서 실행되는 문입니다. variable을 참조할 수 있습니다. 여러 문을 실행하려면 블록 문을 사용하세요.

설명 (Description)

루프는 객체 자신의 모든 열거 가능한 속성과 프로토타입 체인에서 상속받은 속성을 반복합니다(가까운 프로토타입의 속성이 더 먼 프로토타입의 속성보다 우선합니다).

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

  • break는 문 실행을 중단하고 루프 다음의 첫 번째 문으로 갑니다.
  • continue는 문 실행을 중단하고 루프의 다음 반복으로 갑니다.

for...in 루프는 열거 가능하고 symbol이 아닌 속성만 반복합니다. ArrayObject 같은 내장 생성자에서 만든 객체는 Array.prototypeObject.prototype에서 indexOf() 메서드나 ObjecttoString() 메서드 같은 상속된 열거 불가능(non-enumerable) 속성을 가지며, 이들은 for...in 루프에서 방문되지 않습니다.

현대 ECMAScript 사양의 순회 순서는 잘 정의되어 있으며 구현 간에 일관됩니다. 프로토타입 체인의 각 구성 요소 내에서 모든 음이 아닌 정수 키(배열 인덱스가 될 수 있는 것)가 값 오름차순으로 먼저 순회되고, 그 다음 다른 문자열 키가 속성 생성의 시간 오름차순으로 순회됩니다.

삭제, 추가, 수정된 속성

for...in은 다음 방식으로 속성 키를 방문합니다.

  • 먼저 Object.getOwnPropertyNames()와 매우 유사한 방식으로 현재 객체의 모든 자체 문자열 키를 가져옵니다.
  • 각 키에 대해, 같은 값의 문자열이 방문된 적이 없으면 속성 설명자(property descriptor)를 가져와 그 속성이 열거 가능한 경우에만 방문합니다. 그러나 이 속성 문자열은 열거 가능하지 않더라도 방문된 것으로 표시됩니다.
  • 그런 다음 현재 객체가 그 프로토타입으로 대체되고 과정이 반복됩니다.

이것은 다음을 의미합니다.

  • 반복 중에 현재 방문 중인 객체에 추가된 속성은 방문되지 않습니다. 현재 객체의 모든 자체 속성이 이미 미리 저장되었기 때문입니다.
  • 프로토타입 체인의 여러 객체가 같은 이름의 속성을 가지면 첫 번째 속성만 고려되며, 그것이 열거 가능한 경우에만 방문됩니다. 그것이 열거 불가능하면 프로토타입 체인 더 위에 있는 같은 이름의 다른 속성들은 열거 가능하더라도 방문되지 않습니다.

일반적으로 반복 중에는 현재 방문 중인 속성 외에는 객체에 속성을 추가·수정·제거하지 않는 것이 가장 좋습니다.

배열 반복과 for...in

배열 인덱스는 정수 이름을 가진 열거 가능한 속성일 뿐이며 그 외에는 일반 객체 속성과 동일합니다. for...in 루프는 다른 키를 순회하기 전에 모든 정수 키를 순회하며, 엄격히 증가하는 순서로 순회하므로 for...in의 동작이 일반 배열 반복에 가깝습니다. 그러나 for...in 루프는 정수가 아닌 이름을 가진 속성과 상속된 속성을 포함한 모든 열거 가능한 속성을 반환합니다. for...of와 달리 for...in은 배열의 반복자(iterator) 대신 속성 열거를 사용합니다. 희소 배열(sparse array)에서 for...of는 빈 슬롯을 방문하지만 for...in은 방문하지 않습니다.

숫자 인덱스를 가진 for 루프, Array.prototype.forEach() 또는 for...of 루프를 사용하는 것이 더 낫습니다. 인덱스를 문자열이 아닌 숫자로 반환하고 인덱스가 아닌 속성도 피할 수 있기 때문입니다.

자체 속성만 반복하기

객체 자신에 붙은 속성만(프로토타입은 제외) 고려하려면 다음 기법 중 하나를 사용하세요.

  • Object.keys()
  • Object.getOwnPropertyNames()

Object.keys는 열거 가능한 자체 문자열 속성 목록을 반환하고, Object.getOwnPropertyNames는 열거 불가능한 것도 포함합니다.

많은 JavaScript 스타일 가이드와 린터는 for...in 사용을 권장하지 않습니다. 프로토타입 체인 전체를 반복하는데 이는 거의 원하는 바가 아니며, 더 널리 사용되는 for...of 루프와 혼동될 수 있기 때문입니다. for...in은 실질적으로 디버깅 목적으로 가장 유용하며, 객체의 속성을 확인하는 쉬운 방법(콘솔에 출력하거나 다른 방식)입니다.

예제: for...in 사용하기

아래 for...in 루프는 객체의 모든 열거 가능하고 symbol이 아닌 속성을 반복하며 속성 이름과 값을 문자열로 로그합니다.

const obj = { a: 1, b: 2, c: 3 };

for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}

예제: 자체 속성 반복하기

다음 함수는 Object.hasOwn()의 사용을 보여줍니다. 상속된 속성은 표시되지 않습니다.

const triangle = { a: 1, b: 2, c: 3 };

function ColoredTriangle() {
  this.color = "red";
}

ColoredTriangle.prototype = triangle;

const obj = new ColoredTriangle();

for (const prop in obj) {
  if (Object.hasOwn(obj, prop)) {
    console.log(`obj.${prop} = ${obj[prop]}`);
  }
}

예제: 동시 수정 (Concurrent modification)

반복 중에 현재 객체에 추가된 속성은 절대 방문되지 않습니다.

const obj = { a: 1, b: 2 };

for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
  obj.c = 3;
}

가려진(shadowed) 속성은 한 번만 방문됩니다.

const proto = { a: 1 };
const obj = { __proto__: proto, a: 2 };

for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}
// Logs:
// obj.a = 2

Object.defineProperty(obj, "a", { enumerable: false });

for (const prop in obj) {
  console.log(`obj.${prop} = ${obj[prop]}`);
}
// Logs nothing, because the first "a" property visited is non-enumerable.

또한 동작이 명시되지 않아 구현이 사양 알고리즘에서 벗어나는 경향이 있는 경우들도 있습니다. 예: 반복 중 프로토타입 변경, 반복 중 속성 삭제, 반복 중 프로토타입에 열거 가능한 속성 추가, 반복 중 속성의 열거 가능성 변경.

더 알아보기