알고리즘 문제를 풀이 코드에서 String을 순회하는 부분이 있었다.
...
for (const char of str) {
...
resultStr += char;
}
...
다른분께서 for-of for-in을 차이점에 대해서 물어보셨는데 for-of를 사용한 이유에 대해서는 설명할 수 있었지만 디테일한 기술적인 차이에 대해서는 설명할 수 없었다.
그래서 for-in statement, for-of statement에 대해 알아보고 두 가지 방법에 차이점을 알아보자.
for-in statement는 우리가 Object, String, Array 인덱스(properties)를 통하여 값에 접근할 때 주로 사용한다.
const str = "AABBCC";
for (const i in str) {
console.log(str[i]);
}
const obj = { a: 1, b: 2, c: 3 };
for (const property in obj) {
console.log(`${property}: ${obj[property]}`);
}
ecma spec에도 명시되어 있듯이 for-in은 아래와 같은 특징들을 가진다.
하지만 for-in은 객체의 프로토타입 속성들도 순회한다는 문제점을 가지고 있다.
위에 예시에서 우리는 ‘test’ array에 value들에 접근하고 싶어 key를 가지고 왔지만 예상치 못하게 ‘voice’ 속성에도 접근하게 되었다.
이유는 무엇일까?
console.log(test.propertyIsEnumerable('voice'))
위에 결과가 ‘true’가 나오는 것을 확일할 수 있다. 즉, ‘voice’ 속성이 ‘enumerable’ 하기 때문이다.
또 한가지 더 문제점이 있다.
The mechanics and order of enumerating the properties (step 6.a in the first algorithm, step 7.a in the second) is not specified. Properties of the object being enumerated may be deleted during enumeration. If a property that has not yet been visited during enumeration is deleted, then it will not be visited. If new properties are added to the object being enumerated during enumeration, the newly added properties are not guaranteed to be visited in the active enumeration. A property name must not be visited more than once in any enumeration.
ecma spec에 for-in statement 보면 ‘The mechanics and order of enumerating the properties is not specified.’라고 명시되어 있다.
즉, 순서를 보장하지 않는다는 것이다.
for-of statement는 ES6 위에서 언급한 문제들을 가지는 for-in statement를 대체하기 위해 나왔다.
const str = "AABBCC";
for (const char of str) {
console.log(char);
}
const obj = { a: 1, b: 2, c: 3 };
for (const value of obj) {
console.log(`${value}`);
}
우리가 ‘for-in statement’과는 다르게 ‘for-of statement’를 이용하여 객체의 ‘value’에만 쉽게 순차적으로 접근할 수 있는 이유를 순차적으로 살펴보자.
Iteration protocols - JavaScript | MDN
const test = ["Zero", "One", "Two"];
// Symbol.iterator 메소드는 이터레이터를 반환한다.
let iter = array[Symbol.iterator]();
console.log(iter.next()); // {value: 1, done: false}
console.log(iter.next()); // {value: 2, done: false}
console.log(iter.next()); // {value: 3, done: false}
console.log(iter.next()); // {value: undefined, done: true}
그럼 이제 “String을 순회할 때 for-in statement을 사용할까? for-of statement을 사용할까?” 질문에 답을 해보자.
알고리즘 문제에서 내가 사용한 for of문을 분해하면 아래와 같이 동작한다.
const iterator = str[Symbol.iterator]();
for (;;) {
const res = iterator.next();
if (res.done) break;
const char = res.value;
console.log(char);
}
만약, 내가 for-in statemnte로 String을 순회하였다면 순서를 보장해주지 않는다.
특히 내가 선언한 변수가 string이 아니라 array나 object라면 중간에 속성을 정의해주는 코드로 인해 예상치못한 key가 나오는 위험이 도사리고 있다고 생각한다.
나의 선택은 array나 object의 key, value가 동시에 필요한 경우에는 내장 메서드(forEach 등)을 사용하고 ‘value’만 얻고 싶을 때는 이터레이션 프로토콜을 준수하여 순차적으로 ‘value’를 가지고 올 수 있는 for-of statement’를 사용할 것이다!
ECMAScript 2015 Language Specification - ECMA-262 6th Edition