제대로 파는 자바스크립트(JavaScript) - by 얄코
(https://www.inflearn.com/course/제대로-파는-자바스크립트/dashboard)
목차
10-1. Set
10-2. Map
10-3. 이터러블
10-4. 제너레이터
10-5. 중간점검 퀴즈
중복되지 않는 값들의 집합
표준 내장 객체 중 하나
배열과의 차이
- 동일한 값을 여러 번 포함할 수 없음
- 값들의 순서가 무의미함
1. has 메서드: 요소 포함 여부 확인
2. delete 메서드: 요소 제거 + 성공 여부 반환
3. add 메서드: 결과 set을 반환 (∴ 메서드 체이닝이 가능)
4. size 프로퍼티: 요소의 개수
5. keys(), values(), entries() 메서드: 값, 값, [값, 값] 반환
key를 value와 같이 취급 (순서가 의미 없음)
6. clear 메서드: 모든 요소들을 삭제
참조형 데이터의 경우
내용이 같더라도 참조하는 주소가 다르므로 각기 다른 것으로 인식
참조하는 주소가 같으면 같은 것들로 인식
1. for ... of 문
2. 스프레드 문법
3. 디스트럭쳐링
(이터러블과 별개로) forEach 메서드 사용 가능
Set은 배열과 달리 순서 개념이 없으므로 두 번째 인자가 인덱스가 아님(형식을 맞추기 위한 인자로 현재값 사용)
(참고) 7-3 forEach 설명 (https://siwon-swu18.tistory.com/87)
키와 값의 쌍으로 이루어진 컬렉션
표준 내장 객체 중 하나
객체와의 차이
이터러블의 일종이므로 이터러블의 기능 사용 가능
메서드와 프로퍼티 등 기능 차이
객체나 배열 등의 참조값을 키로 사용 가능
키와 값을 자주 변경하는 경우 적합하도록 설계됨
I. 기본 사용법
키의 중복 불허: 해당 키가 있으면 덮어씀
1. has 메서드: 요소 포함 여부 확인
2. get 메서드: 값에 접근
참조값도 키로 사용 가능
객체와 다르게 '참조값'이 키이기 때문에 다른 객체로는 접근 불가
(참고) 5-1의 객체와 비교 (https://siwon-swu18.tistory.com/87)
3. delete 메서드: 요소 제거 + 성공 여부 반환
4. set 메서드: 요소 추가 + 결과 맵을 반환
결과 맵을 반환하므로 메서드 체이닝이 가능
5. size 프로퍼티: 요소의 개수
6. keys, values, entries 메서드: 키, 값, [키, 값] 반환
7. clear 메서드: 모든 요소들을 삭제
1. for ... of 문
2. 스프레드 문법
3. 디스트럭쳐링
(이터러블과 별개로) forEach 메서드 사용 가능
반복, 순회 기능을 사용하는 주체간의 통일된 규격
공통 기능들: for ... of, 스프레드 문법, 배열 디스트럭쳐링
이터러블 iterable: 이터러블 프로토콜을 준수하는 객체
배열, 문자열, Set, Map, arguments 등
키 Symbol.iterator(well-known 심볼)의 값으로 이터레이터를 반환하는 메서드를 가짐
Symbol.iterator() 실행 시 이터레이터 반환
next 메서드를 통해 이터러블을 순회하며 값을 반환
next 메서드 내 프로토콜
- value: 해당 차례에 반환할 값
- done: 순회 종료 여부(마지막 값 반환 다음 차례부터)
*순회가 끝나면 value는 undefined, done은 true 반환
예제 1. 주사위를 열 번 굴리는 이터러블
순회 최댓값(maxCount)을 10으로 설정(이후로는 done: true)
이터러블의 공통 기능들 사용 가능
1. for ... of
2. 스프레드 문법
(참고) 스프레드는 어떻게 작동할까?
const arr = [...iterable];
위의 코드는 내부적으로 아래와 같은 동작을 함
const iterator = iterable[Symbol.iterator]();
let result = iterator.next();
while (!result.done) {
arr.push(result.value);
result = iterator.next();
}
즉, diceTenTimes에서 Symbol.iterator()를 호출 → 내부의 next()를 계속 호출 → 나온 value를 배열에 넣음 → done: true일 때까지 반복하는 것
(참고) diceTenTimes는 "이터러블"이다.
diceTenTimes는 Symbol.iterator 메서드를 구현해서 이터러블 프로토콜을 따르는 일반 객체이다.
이터러블이라는 건 "값을 하나씩 꺼낼 수 있다"는 능력만 있는 것이지, 내부에 배열처럼 값을 저장하고 있는 건 아니다. 따라서 for...of, [...], Array.from() 등을 통해 값을 순회해서 수집해야만 실제 결과를 얻을 수 있다.
3. 배열 디스트럭쳐링
예제 2. 피보나치 이터러블
원하는 최대 횟수의 피보나치 이터러블 생성하기
예제 3. 순번 이터러블 X 이터레이터
이터러블의 역할도 하는 이터레이터 만들기
이터레이터를 겸하는 경우 한 번 순회하면 끝
상태(idx)가 객체에 고정되어있기 때문에 여러 번 반복하면 의도치 않은 결과가 나올 수 있음
새로 생성해야 다시 순회 가능
이터레이터로 사용
이터러블을 반복할 수 있는 이터레이터를 직접 인자로 전달
(참고) fiboIterator()도 이터레이터이므로 인자로 받을 수 있음
제너레이터 generator
함수 내 코드들을 모두 실행하지 않고 외부 호출자에게 제어권을 양도
이터러블과 이터레이터를 간결하게 구현 가능
1. 제너레이터 함수/메서드 선언
function 다음 또는 메서드명 앞에 * (띄어쓰기 위치 무관)
yield 반환값;
2. 제너레이터 객체
제너레이터 함수의 결과값으로 반환
이터레이터이자 이터러블
이터러블로서는 바로 호출해서 사용하는 것이 적합함
next 메서드를 실행하면 다음 yield까지 실행 후 중지
yield의 값을 value로 반환
끝까지 실행 후 done: true
예제 1. 주사위를 열 번 굴리는 제너레이터
예제 2. 피보나치 제너레이터
재생성 없이 다시 순회하면 {value: undefined, done: true} 반환
예제 3. 순번 제너레이터
1. 위 배열의 중복 요소들을 제거하고 가나다순으로 정렬한 뒤 쉼표로 구분하여 출력하는 코드를 작성해보세요.
const array = [
'모', '걸', '도', '개', '윷',
'윷', '걸', '모', '개', '도',
'윷', '모', '걸', '도', '개',
'윷', '모', '걸', '개', '도'
];
예상 답:
const set = new Set(array);
const sortArr = [...set].sort((a, b) => a > b ? 1 : -1);
console.log(sortArr.join(', '));
정답:
const yuts = [...new Set(array)]
.toSorted()
.join(', ');
console.log(yuts);
(참고) sort(), toSorted() : 정렬된 결과를 반환. 인자가 없으면 오름차순 정렬
toSorted(): ES14 추가 기능, 원본 배열 수정X
2. 위 배열의 요소들이 각각 몇 개씩 들어있는지를 나타내는 Map 인스턴스를 만드는 코드를 작성해보세요.
그 중 'apple'은 몇 개인지 출력해보세요. 개수가 가장 적은 순으로 출력해보세요.
const array = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
예상 답:
const map = new Map();
for (item of array) {
map.get(item) ? map.set(item, map.get(item)+1) : map.set(item, 1);
}
console.log(map);
console.log(map.get('apple'));
const arr = [...map];
arr.sort((a, b) => a[1] - b[1]);
arr.forEach( item => console.log(item));
정답:
const countMap = new Map();
array.forEach(item => { // for .. of 대신 forEach 사용
countMap.set(item, (countMap.get(item) || 0) + 1); // set 안에서 || 사용하여 처리
});
console.log(countMap.get('apple'));
[...countMap]
.toSorted((a, b) => a[1] - b[1]) // sort 대신 toSorted 사용
.forEach(i => console.log(i));
3. 인자로 from과 to 이 두 숫자가 주어졌을 때 from으로부터 to까지 1씩 늘려가며 출력하는 이터러블을 반환하는 함수를 만들어보세요. 전자가 항상 더 작다고 가정합니다.
// 사용 예시
const iterable = makeIterable(1, 5);
for (let value of iterable) {
console.log(value); // 1, 2, 3, 4, 5
}
예상 답:
function makeIterable(from, to) {
return {
[Symbol.iterator]() {
return {
next() {
return { value: from, done: from++ > to };
}
}
}
}
}
(참고) from과 to를 직접 사용
단일 이터레이터만 사용할 경우에는 동작상 문제는 없지만
여러 이터레이터를 동시에 생성하거나 재사용할 가능성이 있다면 문제가 생길 수 있음 (주의)
(참고) done이 true인 상태에서도 value에 마지막 값을 포함해서 반환한다는 점에서 일반적인 이터레이터의 규칙과는 어긋남
정답:
function makeIterable(from, to) {
return {
[Symbol.iterator]() {
return {
current: from,
last: to,
// current와 last라는 지역 상태를 내부에 유지함
// 이터레이터가 생성될 때마다 current가 독립적으로 존재하므로 동시 여러 이터레이터 생성 시에도 각각 정상 작동함
next() {
// done이 false인 경우와 true인 경우를 나눠서 작성
if (this.current <= this.last) {
return { done: false, value: this.current++ };
} else {
return {done: true };
}
}
};
}
};
}
(참고) 이터레이터는 done: true일 때 value를 포함하지 않는 것이 표준이다.
4. 3번 문제의 코드를 제너레이터로 작성해보세요.
// 사용 예시
const generator = makeGenerator(1, 5);
for (let value of generator) {
console.log(value); // 1, 2, 3, 4, 5
}
예상 답:
function* makeGenerator(from, to) {
while (from <= to) { // 제너레이터는 from과 to를 직접 사용해도 안전함 (아래 설명 참고)
yield from++;
}
}
정답:
function* makeGenerator(from, to) {
for (let i = from; i <= to; i++) {
yield i;
}
}
(참고) 이터러블과 제너레이터 구현 차이(지역 변수)
- 이터러블 구현에서는 from을 직접 쓰지 말고, 복사해서 써야 한다.
(이유: 이터레이터는 여러 번 호출될 수 있으므로 상태 충돌 방지 필요)
- 제너레이터는 내부 지역변수로 동작하므로 from을 직접 써도 안전하다.
(제너레이터에서의 from은 함수 인자이자 지역 변수임)
(제너레이터를 호출할 때마다 새로운 실행 컨텍스트가 생성되므로, 여러 제너레이터 인스턴스를 생성해도 상태가 충돌하지 않음)
FIN.
[자바스크립트] 섹션 12. 스코프와 바인딩 (0) | 2025.05.03 |
---|---|
[자바스크립트] 섹션 11. 문제들에 대비하기 (0) | 2025.04.13 |
[자바스크립트] 섹션 9. 추가 자료형들 (0) | 2025.04.08 |
[자바스크립트] 섹션 8. 객체 깊게 다루기 (0) | 2025.04.08 |
[자바스크립트] 섹션 7. 배열 (0) | 2025.04.02 |
댓글 영역