함수형 프로그래밍을 거론할 때 꼭 함께 등장하는 것이 바로 이터러블/이터레이터 프로토콜입니다. 이터러블(iterable)은 '순회가능한' 이라는 뜻을 가지고 있고, 이터레이터(iterator)는 '반복자'라는 사전적 정의를 가지고 있는데, 깊게 들어가기 전에 간단한 코드예제부터 살펴보도록 하겠습니다.
const list = [1,2,3,4,5]
for(let i = 0 ; i < list.length ; i++){
console.log(list[i]);
} // 결과 : 1,2,3,4,5
ES5에서는 배열객체, 혹은 유사배열객체가 지니는 length 프로퍼티를 활용해서 for문으로 접근하여 위와같이 요소 하나하나에 접근이 가능했습니다.
그러나 ES6에 이터러블/이터레이터 프로토콜이 포함되면서 다음과 같이 접근이 가능하게 되었습니다.
const list = [1,2,3,4,5];
for(const i of list){
console.log(i);
} // 결과 : 1,2,3,4,5
뭔가 코드의 가독성과 편의성이 올라간 것은 좋은데 내부적으로 어떻게 동작하는걸까?
객체의 종류인 Array, Set, Map 의 순회를 한 번 더 비교해보겠습니다.
-----Array-----
const array = [1,2,3,4,5];
for(let i = 0 ; i < array.length; i++){
console.log(array[i]);
} // 결과 : 1,2,3,4,5
-----Set-----
const set = new Set([1,2,3,4,5]);
for(let i = 0 ; i < set.length; i++){
console.log(set[i]);
} // 결과 : undefined
-----Map-----
const map = new Map([['a',1], ['b',2], ['c',3]]);
for(let i = 0 ; i < map.length; i++){
console.log(map[i]);
} // 결과 : undefined
배열 객체의 경우 key값으로 접근이 가능했지만, Set객체와 Map객체의 경우 key값으로 접근이 불가능한 것을 확인할 수 있습니다. 즉, 기존의 for문과 for-of문의 동작은 '무엇인가 다르다!' 라고 느낌적인 느낌으로 느낄 수 있는 것이죠..!
비밀은 바로 Symbol.iterator() !
예제 코드를 더 살펴보도록 하겠습니다.
const list = [1,2,3];
list[Symbol.iterator] = null;
for(let i = 0 ; i < list.length; i++){
console.log(list[i]);
} // Uncaught TypeError : list is not iterable
3번째 줄에서 list 배열 객체에 Symbol.iterator라는 키값으로 접근하여 값을 null 로 재할당을 했을 때, 기존의 ES5의 순회방식인 for문이 에러를 뿜는 것을 확인할 수 있습니다 .
Set 객체도 확인해 보실까요.
const list = new Set([1,2,3]);
for(const i of list){
console.log(i)
} // 결과 : 1,2,3
list[Symbol.iterator] = null;
for(const i of list){
console.log(i)
} // 결과 : Uncaught TypeError : list is not iterable
Set 객체도 마찬가지로 for of 문으로 순회가 가능한 이터러블이지만(순회 가능한), Symbol.iterator라는 키 값으로 접근하여 null로 값을 재할당 해주면 갑자기 바보가 된 것처럼 순회가 되지 않는 것을 확인할 수 있습니다. (Map도 마찬가지로 동작합니다 . )
그렇다면 여기에서부터 Symbol.iterator가 과연 무엇인가 ? 라는 의문이 들어야 정상이라고 할 수 있습니다 !
스크롤이 길어지고 있으므로 다음 포스팅에서 추가적으로 더 정리하도록 하겠습니다 :)