for...in vs for...of — 객체 순회와 이터러블 순회 구분
for...in과for...of는 생김새가 비슷해서 처음에 정말 헷갈립니다. 면접에서도 자주 나오는 질문인데, 핵심은 "in은 키, of는 값" 입니다.
for...in — 객체의 키를 순회
for...in은 객체의 열거 가능한(enumerable) 프로퍼티 키 를 순회합니다.
const user = { name: "정훈", age: 25, job: "개발자" };
for (const key in user) {
console.log(`${key}: ${user[key]}`);
}
// name: 정훈
// age: 25
// job: 개발자
프로토타입 체인까지 순회하는 문제
function Person(name) {
this.name = name;
}
Person.prototype.greet = function () {
return "안녕!";
};
const person = new Person("정훈");
for (const key in person) {
console.log(key); // "name", "greet" — 프로토타입의 메서드까지 나옴!
}
// hasOwnProperty로 필터링
for (const key in person) {
if (Object.hasOwn(person, key)) {
console.log(key); // "name"만 출력
}
}
이런 문제 때문에 객체 순회에는 Object.keys()를 쓰는 것이 더 안전합니다.
배열에 for...in을 쓰면 안 되는 이유
const arr = ["a", "b", "c"];
for (const index in arr) {
console.log(typeof index); // "string" — 인덱스가 문자열!
console.log(index); // "0", "1", "2"
}
// 배열에 프로퍼티를 추가하면 같이 순회됨
arr.customProp = "oops";
for (const key in arr) {
console.log(key); // "0", "1", "2", "customProp"
}
배열에는 for...of나 배열 메서드를 사용해야 합니다.
for...of — 이터러블의 값을 순회
for...of는 이터러블(iterable) 프로토콜 을 구현한 객체의 값 을 순회합니다.
const arr = ["사과", "바나나", "체리"];
for (const fruit of arr) {
console.log(fruit); // "사과", "바나나", "체리" — 값을 직접 얻음
}
// 문자열도 이터러블
for (const char of "Hello") {
console.log(char); // "H", "e", "l", "l", "o"
}
// Map
const map = new Map([
["a", 1],
["b", 2],
]);
for (const [key, value] of map) {
console.log(`${key}: ${value}`);
}
// Set
const set = new Set([1, 2, 3]);
for (const value of set) {
console.log(value);
}
일반 객체는 for...of로 순회 불가
const obj = { a: 1, b: 2 };
// TypeError: obj is not iterable
for (const value of obj) {
console.log(value);
}
// 대안: Object.entries()와 함께 사용
for (const [key, value] of Object.entries(obj)) {
console.log(`${key}: ${value}`);
}
이터러블이란?
Symbol.iterator 메서드를 가진 객체입니다.
// 내장 이터러블들
const iterables = [
[1, 2, 3], // Array
"hello", // String
new Set([1, 2]), // Set
new Map(), // Map
// NodeList, arguments 등도 이터러블
];
// 커스텀 이터러블 만들기
const range = {
from: 1,
to: 5,
[Symbol.iterator]() {
let current = this.from;
const last = this.to;
return {
next() {
return current <= last
? { value: current++, done: false }
: { done: true };
},
};
},
};
for (const num of range) {
console.log(num); // 1, 2, 3, 4, 5
}
for...of에서 인덱스가 필요할 때
const fruits = ["사과", "바나나", "체리"];
// entries()로 인덱스 + 값
for (const [index, fruit] of fruits.entries()) {
console.log(`${index}: ${fruit}`);
}
// keys()로 인덱스만
for (const index of fruits.keys()) {
console.log(index);
}
// values()로 값만 (기본 동작과 동일)
for (const fruit of fruits.values()) {
console.log(fruit);
}
break, continue, return
for...of와 for...in 모두 break와 continue를 사용할 수 있습니다. forEach와의 중요한 차이점입니다.
const numbers = [1, 2, 3, 4, 5];
// for...of에서 break 사용 가능
for (const num of numbers) {
if (num === 3) break;
console.log(num); // 1, 2
}
// forEach에서는 break 사용 불가!
numbers.forEach((num) => {
if (num === 3) return; // break가 아님, 해당 반복만 스킵
console.log(num); // 1, 2, 4, 5
});
비교 표
| 특성 | for...in | for...of |
|---|---|---|
| 순회 대상 | 열거 가능한 프로퍼티 키 | 이터러블의 값 |
| 반환값 | 키 (문자열) | 값 |
| 배열 | 비권장 (인덱스가 문자열) | 권장 |
| 객체 | 사용 가능 (주의 필요) | 직접 사용 불가 |
| 프로토타입 | 순회함 | 순회 안 함 |
| break/continue | 가능 | 가능 |
어떤 반복문을 쓸까?
// 배열 순회 → for...of 또는 배열 메서드
for (const item of array) { /* ... */ }
array.forEach((item) => { /* ... */ });
// 객체 순회 → Object.keys/entries + for...of
for (const [key, value] of Object.entries(obj)) { /* ... */ }
// 인덱스 기반 반복 → 전통 for
for (let i = 0; i < array.length; i++) { /* ... */ }
// 조건부 중단이 필요 → for...of 또는 전통 for
for (const item of array) {
if (condition) break;
}
**기억하기 **: "
in은 key(인),of는 value(오브)". for...in은 객체 전용(배열에 쓰지 마세요), for...of는 이터러블 전용입니다. 배열은 for...of, 객체는 Object.entries() + for...of가 가장 안전합니다.
댓글 로딩 중...