JS STUDY - iterator

👊0👊·2020년 2월 11일
0

es6 스터디

목록 보기
8/8

for in vs for of

익숙하면서 햇갈리는 두 문장을 비교해보자.

for ..in

for ... in 문은 객체의 모든 non-Symbol, enumerable properties을 반복한다.
즉, object에 있는 항목들을 반복적으로 반환하여 '무언가'를 할 수 있게 해준다.

특이사항은, Symbol은 나타나지 않는다는 것이다.

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

for (const property in object) {
  console.log(`${property}: ${object[property]}`);
}

// expected output:
// "a: 1"
// "b: 2"
// "c: 3"

const myArray = [4,5,6];
for (const index in myArray) {
  console.log(myArray[index]);
}

for in 루프에서 index 값에 할당되는 것은 숫자가 아니라 문자열('0', '1', ....)이다. 게다가 이 루프는 배열의 인덱스만 순회하는 것이 아니라, 프로토타입 체인을 포함한 모든 속성을 순회한다. 원래 for in은 배열이 아니라 객체를 순회하기 위해서 만들어진 루프다.

  • 이 코드에서 index에 할당되는 값들은 "0", "1", "2"과 같은 문자열입니다. 숫자가 아닙니다. 분명 당신이 바라는 것은 문자열 연산 ("2" + 1 == "21")이 아닐 것이기 때문에, 이것은 아무리 봐도 불편한 방법입니다.
  • 루프 구문이 배열 요소들만을 순회하지 않습니다. 대신 누군가에 의해 추가된 확장속성(expando)들도 순회합니다. 예를 들어, 당신이 다루는 배열이 myArray.name이라는 속성을 가지고 있다면, 이 루프는 배열 요소들 말고도 index == "name" 속성을 대상으로 한번 더 실행될 것입니다. 뿐만 아니라 * 배열의 프로토타입 체인(prototype chain)도 순회할 것입니다.
    가장 당혹스러운 것은, 어떤 환경에서는 이 루프의 순회 순서가 무작위라는 점입니다.

그래서 for of 루프가 나왔다.

for ..of

for...of 명령문은 반복가능한 객체 (Array, Map, Set, String, TypedArray, arguments 객체 등을 포함)에 대해서 반복하고 각 개별 속성값에 대해 실행되는 문이 있는 사용자 정의 반복 후크를 호출하는 루프를 생성한다.

const array1 = ['a', 'b', 'c'];

for (const element of array1) {
  console.log(element);
}

// expected output: "a"
// expected output: "b"
// expected output: "c"

이 구문은 지금까지 있었던 배열 순회 방법들 중에서 문법적으로 가장 간결하고, 직접적입니다.
이 구문은 for–in 구문의 모든 단점들을 배제합니다.
forEach() 구문과 달리, break, continue, 그리고 return 구문과 함께 사용할 수 있습니다.

정리

for–in 루프 구문은 객체의 속성들을 순회하기 위한 구문입니다.
for–of 루프 구문은 배열의 요소들, 즉 data를 순회하기 위한 구문입니다.
그런데 그게 전부가 아닙니다.

다른 컬렉션

아까 for of는 반복가능한 객체들에 사용할 수 있다하였습니다.
배열같은 객체들을 대상으로 사용할 수 있습니다. DOM NodeList 같은 객체들 말이죠.

for–of 구문은 문자열도 다룰 수 있습니다.

for (var chr of "hello") {
  alert(chr);
}

또한 Map, Set에서 사용가능합니다.

var uniqueWords = new Set(words);
for (var word of uniqueWords) {
  console.log(word);
}

마법을 걷어내고

for–of 구문도 어떤 메소드를 반복 호출하는 방식으로 동작합니다. Array, Map, Set, 그리고 우리가 언급했던 모든 객체들은 공통적으로 이터레이터(iterator) 메소드를 제공합니다.
그리고 이터레이터 메소드를 제공할 수 있는 객체가 더 있습니다. 바로 당신이 원하는 모든 객체입니다.

당신이 어떤 객체에든 myObject.toString() 메소드를 추가하면 JS가 이를 통해 해당 객체를 문자열로 변환하는 방법을 알아내는 것과 마찬가지로, 어떤 객체에든 myObjectSymbol.iterator 메소드를 추가하면 JS는 해당 객체를 어떻게 순회(loop)해야 하는지 알아냅니다.

예를 들어, 당신이 jQuery를 사용하고 있다고 가정해봅시다. 비록 .each() 메소드를 써도 좋겠지만 for–of 구문을 써서 jQuery 객체를 다뤄봅시다. 방법은 다음과 같습니다.

// jQuery 객체는 배열(array)과 유사하므로,
// Array와 똑같은 이터레이터 메소드를 제공합니다
jQuery.prototype[Symbol.iterator] =
  Array.prototype[Symbol.iterator];

좋습니다. 당신이 무슨 생각을 하는지 알 것 같습니다. [Symbol.iterator] 구문의 문법이 이상하다고 생각되지 않나요? 이 구문은 어떤 뜻일까요? 이 구문에서 중요한 것은 메소드의 이름입니다. 표준화 위원회가 이 메소드를 .iterator() 메소드라고 명명해도 좋았을 것입니다. 하지만 기존에 작성된 코드의 어떤 객체가 이미 .iterator()라는 메소드를 갖고 있다면 문제가 무척 복잡해집니다. 그래서 표준화 위원회는 평범한 문자열로 메소드 이름을 정하는 대신 symbol을 사용하기로 결정했습니다.

Iterable, Iterator

그런데 더 중요한 것은, for of 문이 배열만을 위한 것이 아니라, 모든 '순회가능한(iterable)' 객체를 상대로 사용할 수 있다는 것이다.

Iterable

그렇다면 '순회가능한' 객체란 무엇일까? 바로 Symbol.iterator 심볼을 속성으로 가지고 있고, 이터레이터 객체를 반환하는 객체를 뜻한다. 이런 스펙을 이터러블 프로토콜 이라고 하고 이 프로토콜을 지킨 객체를 이터러블 객체라고 한다.

Iterator

그럼 이터러블 객체가 [Symbol.iterator]() 메소드로 반환하는 이터레이터 객체는 무엇일까? next() 메소드를 구현하고 있고, done과 value 속성을 가진 객체를 반환하는 객체이다. 이런 스펙을 이터레이터 프로토콜이라고 한다.

for of 루프는 순회를 시작하기 전, [Symbol.iterator]() 메소드를 호출하여 이터레이터 객체를 얻은 후, 순차적으로 next() 메소드를 호출하면서 하나씩 순회하는 것이다. 보통 이터러블 프로콜과 이터레이터 프로토콜을 하나의 객체에 모두 구현하는 것이 일반 적이다. 예를 들면 다음과 같다.

var zeroesForeverIterator = {
    [Symbol.iterator]: function () {
        return this;
    },
    next: function () {
        return {done: false, value: 0};
    }
};

영원히 0을 반환하는 무한 이터레이터이다.

이터레이터 객체는 경우에 따라 .return() 메소드와 .throw(exc) 메소드를 제공할 수도 있습니다. for–of 루프는 예외(exception)나, break 구문, return 구문으로 인해 조건보다 일찍 루프를 벗어날 때 .return() 메소드를 호출합니다. 이터레이터가 사용하던 자원을 해지해야 할 필요가 있다면 .return() 메소드에서 정리합니다. 대부분의 이터레이터 객체는 .return() 메소드를 구현할 필요가 없을 것입니다

profile
ㅎㅎ

0개의 댓글