String을 순회할 때 for-in statement을 사용할까? for-of statement을 사용할까?

Junbro·2022년 7월 30일
1
post-thumbnail

알고리즘 문제를 풀이 코드에서 String을 순회하는 부분이 있었다.

...
for (const char of str) {
  ...

  resultStr += char;
}
...

다른분께서 for-of for-in을 차이점에 대해서 물어보셨는데 for-of를 사용한 이유에 대해서는 설명할 수 있었지만 디테일한 기술적인 차이에 대해서는 설명할 수 없었다.

그래서 for-in statement, for-of statement에 대해 알아보고 두 가지 방법에 차이점을 알아보자.

1. for-in 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은 아래와 같은 특징들을 가진다.

  • 객체의 열거 가능한 ‘속성들’을 순회할 수 있도록 해준다.
  • 객체의 key 값에 접근 가능

하지만 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.’라고 명시되어 있다.

즉, 순서를 보장하지 않는다는 것이다.

2. for-of statement

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’에만 쉽게 순차적으로 접근할 수 있는 이유를 순차적으로 살펴보자.

  • ES6 문법에 이터레이션 프로토콜을 준수한 객체를 이터레이터가 추가되었다.

Iteration protocols - JavaScript | MDN

  • String, Array, Map, Set 등 자바스크립트 자료구조에 내장되었다.

  • for-of 문은 사용하면 이 ‘내장된 이터레이터’가 반환된다.

  • 반환된 이터레이터 객체는 next() 메서드를 활용하여 데이터를 순회한다.
  • 이터레이터 객체의 ‘value’ 프러퍼티의 값을 for of 문 변수에 할당한다.
  • 그리고 이터레이터 객체의 ‘done’프러퍼티가 ‘true’이면 데이터를 모두 순회하였다고 판단하여 중단한다.
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}

3. 결론

그럼 이제 “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

ECMAScript Language Specification - ECMA-262 Edition 5.1

17. The for-of loop

for...in - JavaScript | MDN

profile
정보를 공유하겠습니다 😀

0개의 댓글