ES6+함수형-ES6에서의 순회와 이터러블:이터레이터 프로토콜

Min·2021년 2월 18일
0

JavaScript

목록 보기
18/18
post-thumbnail

함수형 프로그래밍과 JavaScript ES6+ 강의를 수강하며 정리한 내용입니다.

1. 기존과 달라진 ES6에서의 리스트 순회

  • for i++
  • for of

1) 기존 ES5

const list = [1, 2, 3]; // 배열 순회
for (var i = 0; i < list.length; i++) {
  log(list[i]);
}

const str = 'abc'; // 유사배열 순회
for (var i = 0; i < str.length; i++) {
   log(str[i]);
}

2) ES6

for (const a of list) {
  log(a);
}
for (const a of str) {
  log(a);
}

2. Array, Set, Map을 통해 알아보는 이터러블/이터레이터 프로토콜

배열은 키를 통해 키에 매핑되는 값을 조회 가능하지만
set과 map은 불가능하므로
for...of 문은 기존 ES6의 배열 순회와 다르게 동작한다.

arr[0] // 1
set[0] // undefined
map[0] // undefined

Symbol은 어떤 객체의 key로 사용될 수 있으며
Key에는 함수가 들어있으며
null로 키를 비워버릴때 에러 발생
-> for...of 문과 symbol.iterator의 담겨져 있는 함수가 연관성이 있다.

arr[Symbol.iterator] // ƒ values() { [native code] }
set[Symbol.iterator] // ƒ values() { [native code] }
map[Symbol.iterator] // ƒ entires() { [native code] }

arr[Symbol.iterator] = null; // arr is not iterable

이터러블/이터레이터 프로토콜

  • 이터러블: 이터레이터를 리턴하는 Symbol.iterator 를 가진 값
  • 이터레이터: { value, done } 객체를 리턴하는 next() 를 가진 값
  • 이터러블/이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자 등과 함께 동작하도록한 규약
const arr = [1, 2, 3];

// 이터러블
arr; // [1, 2, 3]

// arr가 [Symbol.iterator]를 가지고 있다.
arr[Symbol.iterator]; // ƒ values() { [native code] }

// 실행했을 때 이터레이터를 리턴
let iterator = arr[Symbol.iterator](); // Array Iterator {}

// 이터레이터는 { value, done } 객체를 리턴하는 next() 를 가지고 있다.
iterator.next(); // {value: 1, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: 3, done: false}
iterator.next(); // {value: undefined, done: true}

// value가 1, 2, 3 후 done이 true가 되면 순회를 멈춘다.
for (const a of iter1) log(a); 

1) Array

const arr = [1, 2, 3];

let iter1 = arr[Symbol.iterator](); // {value: 1, done: false}
iter1.next();
for (const a of iter1) log(a); // 2, 3

2) Set

const set = new Set([1, 2, 3]);
for (const a of set) log(a);

3) Map

const map = new Map([['a', 1], ['b', 2], ['c', 3]]);

// 값이 이터레이터
var it = map.values(); // [Map Iterator] { 1, 2, 3 }

// 이터레이터로 만든것이 Symbol.iterator을 가지고 있다.
// 그러므로 for...of 문에서 Symbol.iterator을 가지고 가지고 실행한다.
it[Symbol.iterator]; // ƒ [Symbol.iterator]() { [native code] }

// 자기 자신을 그대로 리턴한다.
var it2 = it[Symbol.iterator]();

it2.next(); // {value: 1, done: false}
it2.next(); // {value: 2, done: false}
it2.next(); // {value: 3, done: false}
it2.next(); // {value: undefined, done: true}
const map = new Map([['a', 1], ['b', 2], ['c', 3]]);

// 이터레이터의 key를 리턴
for (const a of map.keys()) log(a); // a, b, c

// 이터레이터의 value를 리턴
for (const a of map.values()) log(a); // 1, 2, 3

// 이터레이터의 key와 value를 리턴
for (const a of map.entries()) log(a); // ['a', 1], ['b', 2], ['c', 3]
console.clear();

3. 사용자 정의 이터러블, 이터러블/이터레이터 프로토콜 정의

사용자 정의 이터러블을 통해 알아보기

const iterable = {
  [Symbol.iterator]() {
    let i = 3;
    return {
      next() {
        return i == 0 ? {done: true} : {value: i--, done: false};
      },
    }
  }
};

let iterator = iterable[Symbol.iterator]();

iterator.next(); // {value: 3, done: false}
iterator.next(); // {value: 2, done: false}
iterator.next(); // {value: 1, done: false}
iterator.next(); // {done: true}

for (const a of iterator) log(a);
const iterable = {
  [Symbol.iterator]() {
    let i = 3;
    return {
      next() {
        return i == 0 ? {done: true} : {value: i--, done: false};
      },
      [Symbol.iterator]() {
        return this;
      }
    }
  }
};

const arr2 = [1, 2, 3];
let iter2 = arr2[Symbol.iterator]();
iter2.next();
iter2[Symbol.iterator]() == iter2;
for (const a of iter2) log(a);

for (const a of document.querySelectorAll('*')) log(a);
const all = document.querySelectorAll('*');
let iter3 = all[Symbol.iterator]();
log(iter3.next());
log(iter3.next());
log(iter3.next());

4. 전개 연산자

const a = [1, 2];
// a[Symbol.iterator] = null;
log([...a, ...arr, ...set, ...map.keys()]);
profile
slowly but surely

0개의 댓글