본문으로 바로가기

for-in 루프와 열거

for-in 루프는 배열이 아닌 객체를 순회할 때만 사용해야 합니다.

for-in 으로 루프를 도는 것을 열거(enumeration)라고도 합니다.

자바스크립트에서 배열은 곧 객체이기 때문에 기술적으로는 배열을 순회할 때에도 fon-in 루프를 사용할 수 있지만 권장사항은 아닙니다.

배열 객체에 사용자가 정의한 기능이 추가되었다면 논리적인 오류가 발생할 수 있는 여지가 있습니다.

또한 for-in 에서는 프로퍼티를 열거하는 순서가 정해져 있지 않습니다.

따라서 배열에는 일반적인 for 문을 사용하고 객체에만 for-in 문을 사용하는 것이 바람직합니다.




객체의 프로퍼티 순회하기

객체의 프로퍼티를 순회할 때는 프로토타입 체인을 따라 상속되는 프로퍼티들을 걸러내기 위해 hasOwnProperty() 메서드를 사용해야 합니다.

다음 예제를 살펴봅니다.

JavaScript
// 객체 정의
var person = {
	hand: 2,
	legs: 2,
	head: 1
};

// 코드 어딘가에서 모든 객체에 아래와 같은 메서드 하나가 추가되었다고 해보자.
if (typeof Object.prototype.clone === 'undefined') {
	Object.prototype.clone = function () {
		// 메소드 로직 생략
	};
}

이 예제에서는 객체 리터럴을 사용하여 person 이라는 이름의 간단한 객체를 정의했습니다.

person 을 정의하기 전이나 후, 어디선가 Object 프로토타입에 clone() 이라는 이름의 메서드가 편의상 추가되었다고 했을때 프로토타입 체인의 변경 사항은 실시간으로 반영되기 때문에 자동적으로 모든 객체가 이 새로운 메서드를 사용할 수 있습니다.

person 을 열거할 때 clone() 메서드가 나오지 않게 하려면 프로토타입 프로퍼티를 걸러내기 위해 hasOwnProperty() 를 호출해야 합니다.

이렇게 걸러내지 않으면 clone() 이 나오게 되는데, 대부분의 경우 이러한 동작 방식은 바람직하지 않습니다.

JavaScript
// 1. for-in # 좋은 패턴
for (var prop in person) {
	if (person.hasOwnProperty(prop)) { // 프로토타입 프로퍼티를 필터링한다.
		console.log(prop, ":", person[prop]);
	}
}
// 콘솔에 출력결과는 다음과 같다.
/*
	hand : 2
	legs : 2
	head : 1
*/


// 2. for-in # 안티 패턴(hasOwnProperty 를 확인하지 않은 경우)
for (var prop in person) {
	console.log(prop, ":", person[prop]);
}
// 콘솔에 출력결과는 다음과 같다.
/*
 hand : 2
 legs : 2
 head : 1
 clone : () {
	// 메소드 로직 생략
 }
*/

Object.prototype 에서 hasOwnProperty() 를 호출하는 것도 또 하나의 패턴입니다.

JavaScript
for (var prop in person) {
	if (Object.prototype.hasOwnProperty.call(person, prop)) {
		console.log(prop, ":", person[prop]);
	}
}

이 방법은 person 객체가 hasOwnProperty 를 재정의하여 덮어썼을 경우에도 활용할 수 있다는 장점이 있습니다.

그리고 프로퍼티 탐색이 Object 까지 멀리 거슬러 올라가지 않게 하려면 지역 변수를 사용하여 이 메서드를 아래 코드와 같이 "캐시"하면 됩니다.

JavaScript
var prop,
	hasOwn = Object.prototype.hasOwnProperty;

for (prop in person) {
	if (hasOwn.call(person, prop)) { // 걸러내기
		console.log(prop, ":", person[prop]);
	}
}



엄밀히 말하면 hasOwnProperty() 를 사용하지 않았다고 해서 에러가 발생하지는 않습니다.

작업 내용에 따라, 또 개발자가 코드에 확신을 가지고 있다면 hasOwnProperty() 를 사용하지 않아도 되며, 이 경우 루프 속도도 약간 개선됩니다.

그러나 객체와 객체 프로토타입 체인의 내용을 보장할 수 없다면, 그냥 hasOwnProperty() 확인을 추가하는 편이 좀더 안전할 것입니다.



위 코드를 형식상 변형을 가하자면(JSLint 를 통하진 못하겠지만) 괄호를 생략하고 if 문을 같은 줄에 넣는 방법이 있습니다.

이렇게 하면 루프 표현식이 완결된 하나의 생각으로 읽히는 장점이 있습니다.

즉, "엘리먼트가 프로퍼티 X를 가지고 있는 경우, X를 가지고 어떤 일을 수행한다" 라는 식입니다.

또한 루프의 핵심부에 이르기까지 들여쓰기가 줄어들게 됩니다.

JavaScript
var prop,
	hasOwn = Object.prototype.hasOwnProperty;

for (prop in person) if (hasOwn.call(person, prop)) { // filter
	console.log(prop, ":", person[prop]);
}



Jaehee's WebClub