모던 자바스크립트 Deep Dive, 이웅모
순회가능한 자료구조를 위해 ECMAScript 사양에 정의하여 미리 약속한 규칙
이터레이션 프로토콜의 등장 이전에는 각 자료 구조가 각각의 순회 방법에 따라 데이터를 순회함(배열과 인덱스같이)
→ 효율적이지 않음, 이를 소비하는 우리가 각각의 자료구조에 대해 순회방식을 고려해주어야함
그래서 데이터 공급자가 다양한 자료구조들이 이터레이션 프로토콜을 준수하도록 설정했고 소비자는 이에 맞추어 이터레이션 프로토콜을 지원하는 순회 방식만을 사용하면 됨
데이터 소비자와 공급자를 연결하는 인터페이스 역할
이터러블 프로토콜
객체 에 상속받은 Symbol.iterator
이나 직접 구현한 [Symbol.iterator](){...}
는 호출 시에 아래 프로토콜을 준수한 이터레이터를 반환 → 이런 규칙이 이터러블 프로토콜, 이를 만족하는 객체 가 이터러블
이터레이터 프로토콜
객체 는 next
메서드를 소유하며 이 메서드는 이터러블을 순회하며 value
, done
프로퍼티를 가지는 이터레이터 리절트 객체를 반환 → 이런 규칙이 이터레이터 프로토콜, 이를 만족하는 객체 가 이터레이터(포인터 역할)
{ // 객체임
next() {
.. // 보통 연산 내용이 여기 들어가는 듯, next가 한 번 호출되면 값이 다음으로 넘어가는 형태
return {value: 값, done: 순환 끝 여부};
}
}
이터러블의 경우 for...of
문으로 순회할 수 있으며 디스트럭처링 할당 가능
따로 설정해주지 않아도 되는 이터러블들 → 바로 사용가능
빌트인 이터러블 | Symbol.iterator |
---|---|
Array | Array.prototype[Symbol.iterator] |
String | String.prototype[Symbol.iterator] |
Map | Map.prototype[Symbol.iterator] |
Set | Set.prototype[Symbol.iterator] |
TypedArray | TypedArray.prototype[Symbol.iterator] |
arguments | arguments[Symbol.iterator] |
DOM 컬렉션 | NodeList.prototype[Symbol.iterator] HTMLCollection.prototype[Symbol.iterator] |
일반 객체에 [Symbol.iterator]
메서드를 구현
const obj = {
[Symbol.iterator](){
let num = 0;
const max = 10;
return {
// next 메소드를 가지는 이터레이터 반환
next(){
num++;
return { value: num, done: num >= max }; // 리절트 객체
}
};
}
}
다음과 같이 this를 이용하면 이터러블이면서 이터레이터인 객체를 반환하는 함수를 만들 수 있음
const count = function(start){
let num = start;
// 객체 반환
return {
// 현재 객체([Symbol.iterator], next 메소드 존재) 반환하는 [Symbol.iterator] 메서드
[Symbol.iterator](){ return this; }
// next 메소드
next(){
num++;
return { value: num, done: num >= max }; // 리절트 객체
}
};
};
for...of
문for(변수선언문 of 이터러블) { .. }
done 값이 true가 될 때까지 반복, true인 객체의 value는 사용되지 않음
다른 반복문들과 크게 차이는 없으나 이터러블에만 사용할 수 있음
리절트 객체에 done
프로퍼티를 제외하면 무한 이터러블을 구현할 수 있음
const count = function(start){
let num = start;
return {
[Symbol.iterator](){ return this; }
next(){
num++;
return { value: num }; // 종료조건 없음 -> 무한
}
};
};
const [a,b,c] = count(10);
console.log(a, b, c); // 10 11 12
지연평가는 메모리를 미리 확보한 다음 이후 데이터를 공급하는 것이 아니라
실제 변수를 사용해야 할 때 메모리를 확보하고 데이터를 생성하여 할당하는 기법, 결과가 필요할 때 까지 평가를 늦추는 기법 → next
메서드가 호출될 때 데이터가 생성
이터러블한정 문법
이터러블의 값들을 펼쳐서 목록으로 만듦
값이 아님, 그렇지만 함수의 인수로는 전달 가능
const arr = [1, 2, 3];
// 아래 두 줄은 동일
anyFunction.apply(null, arr);
anyFunction.call(null, ...arr);
배열
// 얕은 복사
const arr1 = [...arr]; // [1, 2, 3]
const arr2 = [7, 8];
const mergedArr = [...arr1, ...arr2]; // [1, 2, 3, 7 ,8]
스프레드 프로퍼티 제안
공식적이진 않지만 이터러블이 아닌 일반객체의 프로퍼티에 대해서 스프레드 문법을 적용하는 것이 제안되어 있음
const obj1 = { foo: 'bar', x: 42 };
const obj2 = { foo: 'baz', y: 13 };
const clonedObj = { ...obj1 }; // 얕은 복사
// 동일한 프로퍼티가 있느느 경우 뒤쪽에 있는 값으로 할당
const mergedObj = { ...obj1, ...obj2 }; // { foo: "baz", x: 42, y: 13 }
와 다름
Rest 파라미터는 인수로 전달된 값의 목록을 모아 배열을 만드는 것이고
스프레드 문법은 배열을 포함하는 이터러블들을 펼쳐 값의 목록을 만드는 것
function f(...rest){
console.lot(rest);
}
f(...[1,2,3]);
말 그대로 구조화 되어있는 이터러블 또는 객체를 비구조화시켜 1개 이상의 변수에 할당하는 것
우변에 무조건 이터러블이 와야함
배열의 인덱스, 즉 순서를 기준으로 할당함
const arr = [1, 2, 3];
const [a, b] = arr; // a=1, b=2
const [a1, a2, a3, a4] = arr; // a1=1, a2=2, a3=3, a4=undefined (부족하면 undefined 할당)
const [b1, , b2] = arr; // a1=1, a2=3
// 기본값 지정 가능하나 이터러블 값이 우선시됨
const [c1, c2, c3=11, c4=10] = arr; // a1=1, a2=2, a3=3, a4=10
// rest요소 사용가능, 가장 뒤에 와야함
const [d,...D] = arr; // d=1, D=[2, 3]
프로퍼티 키를 이용하여 할당
const info = { lang:'javascript', book:'deepdive', hi:5};
// 순서와 갯수상관 없으나 프로퍼티 키가 일치해야함
const { book, lang } = info; // book='deepdive', lang='javascript'
const { lang:l , hi:hello } = info; // l='javascript', hello=5
// rest프로퍼티 사용가능, 가장 뒤에 와야함
const { book:BOOK,...obj } = info; // BOOK='deepdive', obj={ lang:'javascript', hi:5 }