기존과 다른 ES6에서 리스트 순회

taehyung·2025년 5월 19일

JavaScript

목록 보기
14/15

문법적 차이

/**
* ES5
*/
const list = [1,2,3]
const str = 'list'

for(var i = 0; i < list.length; i++){ // 배열
	console.log(list[i])
}

for(var i = 0; i < str.length; i++){ // 유사 배열
	console.log(str[i])
}
/**
* ES6
*/
const list = [1,2,3]

for (const el of list){
	console.log(el)
}

ES6 규격이 되면서 순회 문법이 간결해지고 선언적으로 변했는데 단순히 문법의 변화 이상의 큰 의미가 있다.

ES6 에서 리스트 순회를 어떻게 추상화하고 개발자에게 사용하게 했는지 상세히 알아보자!


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

  • 이터러블: 이터레이터를 리턴하는 [Symbol.iterator]() 메서드를 포함한 값 ( 배열, set, map 등)

    반복가능한 값들이 Symbol.iterator 메서드를 가진게 아니라 Symbol.iterator 메서드가 있기에 반복이 가능해지는 것인듯

  • 이터레이터: 이터러블의 Symbol.iterator 메서드를 실행했을 때 반환한 값으로 { value, done } 객체를 리턴하는 next() 를 가진 값

  • 이터러블/이터레이터 프로토콜: 이터러블을 for...of, 전개 연산자 등과 함께 동작하도록한 규약

자바스크립트에서 순회 가능한 데이터 타입은 총 3 종류로 Array, Set, Map 이 있다.

  • Set : 이터러블을 인자로 받아 Set 인스턴스로 만들어준다. ( 프로토타입 Set이며 Symbol.iterator 메서드를 포함한 반복 가능한 객체이다. )

  • Map : key와 value로 이루어진 이터러블을 인자로 받아 Map 인스턴스로 만들어준다.

const arr = [1,2,3]
const set = new Set([1,2,3])
const map = new Map([['name','김태형'],['age',33],['height',175]])

console.log(arr[0]) // 1
console.log(set[0]) // undefined
console.log(map[0]) // undefined

for(const v of arr) console.log(v) // 1 2 3
for(const v of set) console.log(v) // 1 2 3
for(const v of map) console.log(v) // ['name','김태형] ['age',33] ['height',175]

Array는 두 방법 모두 허용되나 Set과 Map은 for...of 문 만 허용된다.

왜그럴까?

Set, Map 이 [index] 로 요소에 접근이 불가하다는 것은 for...of 문의 작동 방식이 기존 ES5 에서의 배열 순회 방법과 차이가 있다는것을 알 수 있다.

즉, 배열의 index가 아닌 다른 형태의 순회 방법을 사용하고 있다는 것인데 여기에서 바로 이터러블/이터레이터 프로토콜을 사용한 순회를 한다는 것을 알 수 있다.

Array 를 console로 찍어보면 내부에 Symbol.iterator 메서드를 포함한게 보인다.

Symbol 은 ES6 에서 새로 정의된 기본 타입 데이터로 객체와 배열의 키로 사용할 수 있다.

위에 살펴본 사진에서 배열은 Symbol.iterator 을 내부에 가지고 있으므로

const arr = [1,2,3]
console.log(arr[Symbol.iterator]) // values() { [native code] } 함수 반환
console.log(arr[Symbol.iterator]()) // Array Iterator {} 이터레이터 반환

이렇게 반환된 이터레이터는 next 메서드를 통해 이터레이터를 순회할 수 있다.

  • next(){} : {value : 순회중인 값 , done : false} 을 반환하고 다음 순회할 값으로 포인터를 이동한다.

순회하다 마지막 요소에 다다르면 done 을 true로 변경하며 더이상 순회하지 않게 된다.

for..of 는 이터러블/이터레이터 프로토콜을 지키는 순회 방식을 사용합니다.

const arr = [1,2,3]
arr[Symbol.iterator] = null // 이터레이터 강제로 제거하기

for(const v of arr) console.log(v) // 에러발생 ! Uncaught TypeError: arr is not iterable

결론
for...of 문법은 이터러블/이터레이터 프로토콜을 준수하는 순회 방식을 사용한다.
Array도 이터러블/이터레이터 프로토콜을 준수한다. 다만, Set, Map 은 이터러블/이터레이터 프로토콜만을 준수하는 데이터 타입이기 때문에 Set, Map 은 [0] 과 같은 인덱스형 접근이 불가능한것.


이터레이터는 이터러블이다

이터러블은 Symbol.iterator 메서드를 포함한 값이라고 했는데요. 그리고 Symbol.iterator 메서드를 실행하면 이터레이터를 반환한다고 했습니다. 그런데 이렇게 반환된 이터레이터 객체 또 한 Symbol.iterator 메서드를 포함하고있습니다.

그렇다면 이터레이터는 이터러블 이겠죠? 그럼 Array, set, map 각 요소의 {value, done} 을 반환하는 것과 어떤점이 다를까요?

const arr = [1,2,3]
const iter = arr[Symbol.iterator]() 
console.log(arr[Symbol.iterator]) //[Symbol.iterator]() { [native code] }
console.log(arr[Symbol.iterator]()) //Array Iterator {}

console.log(iter.next()) // {value:1, done:false}

const iter2 = iter[Symbol.iterator]()
console.log(iter2.next()) // {value:2, done:false}

console.log(iter === iter2) //true
// 자바스크립트 Array Iterator {} 객체의 Symbol.iterator 는 자기 자신을 반환하도록 설계되어있다!

[Symbol.iterator]() {
  return this;
}

즉, 이터레이터의 Symbol.iterator 메서드는 자기 자신을 반환한다.

이터레이터는 1회성 객체이다.

당연한 이야기지만 이터레이터도 객체기 때문에 다른 변수에 할당하면 원본을 공유한다.

const arr = [1,2,3]
const iter = arr[Symbol.iterator]() 

for(const v of iter)console.log(v) // 1, 2, 3

const iter2 = iter[Symbol.iterator]()
console.log(iter2.next()) // {value:undefiend, done:true}
profile
Front End

0개의 댓글