본문 바로가기

2022-2 웹개발 스터디

[모던 JS] CH37. Set과 Map, CH42. 비동기 프로그래밍

CH37. Set과 Map

37.1 Set

- Set: 중복되지 않는 유일한 값들의 집합

- 배열과 유사하지만 차이가 있음

- 수학적 집합의 특성과 일치함 (수학적 집합을 구현하기 위한 자료구조)

37.1.1 Set 객체의 생성

- Set 생성자 함수로 생성

- 인수를 전달하지 않으면 빈 Set 객체 생성

- 이터러블을 인수로 전달받아 Set 객체를 생성함

- 이터러블의 중복된 값은 Set 객체에 요소로 저장되지 않음

const set = new Set();
console.log(set); // Set(0) {}

conset set2 = new Set('hello');
console.log(set2); // Set(4) {"h", "e", "l", "o"}

37.1.2 요소 개수 확인

- Set.prototype.size 프로퍼티 사용

- size프로퍼티는 getter 함수만 존재하는 접근자 프로퍼티

: size 프로퍼티에 숫자를 할당해서 Set 객체의 요소 개수를 변경할 수 없음

const {size} = new Set([1, 2, 3, 3]);
console.log(size); // 3

const set = new Set([1, 2, 3]);
console.log(Object.getOwnPropertyDescriptor(Set.prototype, 'size'));

set.size = 10; // 무시 됨
console.log(set.size); // 3

37.1.3 요소 추가

- Set.prototype.add 메서드 사용

- add 메서드를 호출한 후에 add 메서드를 연속적으로 호출할 수 있음

- 중복된 요소의 추가는 허용하지 않음 (에러 발생하지 않고 무시됨)

- 자바스크립의 모든 값을 요소로 저장할 수 있음

const set = new Set();
console.log(set);

set.add(1).add(2).add(2);
console.log(set); // Set(2) {1, 2}

37.1.4 요소 존재 여부 확인

- Set.prototype.has 메서드 사용

const set = new Set([1, 2, 3]);

console.log(set.has(2)); // true
console.log(set.has(4)); // false

37.1.5 요소 삭제

- Set.prototype.delete

- Set 객체에는 순서에 의미가 없으므로, 인덱스가 아니라 삭제하려는 요소 값을 인수로 전달해야 함

- 만약 존재하지 않는 요소를 삭제하려고 하면 에러 없이 무시됨

- 연속적으로 호출 불가

const set = new Set([1, 2, 3]);

set.delete(2);
set.delete(5); // 무시됨
console.log(set); // Set(2) {1, 3}

37.1.6 요소 일괄 삭제

- Set.prototype.clear

- 언제나 undefined 반환

37.1.7 요소 순회

- Set.prototype.forEach

: 첫 번째 인수 - 현재 순회 중인 요소값

: 두 번째 인수 - 현재 순회 중인 요소값

: 세 번째 인수 - 현재 순회 중인 Set 객체 자체

- 첫 번째 인수와 두 번째 인수가 같은 값인 이유는 Array 메서드와 인터페이스를 통일하기 위함

- for ... of 문으로도 순회 가능, 스프레드 문법과 배열 디스트럭처링의 대상이 될 수도 있음

const set = new Set([1, 2, 3]);

set.forEach((v, v2, set) => console.log(v, v2, set));

/*
1 1 Set(3) {1, 2, 3}
2 2 Set(3) {1, 2, 3}
3 3 Set(3) {1, 2, 3}
*/

37.1.8 집합 연산

교집합

Set.prototype.intersection = function (set) {
    return new Set([...this].filter(v => set.has(v)));
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.intersection(setB)); // Set(2) {2, 4}

합집합

Set.prototype.union = function (set) {
    return new Set([...this, ...set]);
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.union(setB)); // Set(4) {1, 2, 3, 4}

차집합

Set.prototype.difference = function (set) {
    return new Set([...this].filter(v => !set.has(v)));
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

console.log(setA.difference(setB)); // Set(2) {1, 3}
console.log(setB.difference(setA)); // Set(0) {}

부분 집합과 상위 집합

Set.prototype.isSuperset = function (subset) {
	const supersetArr = [...this];
    return [...subset].every(v => supersetArr.includes(v));
};

const setA = new Set([1, 2, 3, 4]);
const setB = new Set([2, 4]);

// setA가 setB의 상위 집합인지 확인
console.log(setA.isSuperset(setB)); // true

// 그 반대 확인
console.log(setB.isSuperset(setA)); // false

37.2 Map

- 키와 값의 쌍으로 이루어진 컬렉션

- 객체와 유사하지만 차이가 있음

37.2.1 Map 객체의 생성

- Map 생성자 함수로 생성

- 인수를 전달하지 않으면 빈 Map 객체가 생성됨

- 인수로 전달되는 이터러블은 키와 값의 쌍으로 이루어진 요소로 구성되어야 함

- 중복된 키를 갖는 요소가 존재하면 값이 덮어써짐

const map = new Map();

console.log(map); // Map(0) {}

37.2.2 요소 개수 확인

- Map.prototype.size

- Set과 같은 맥락으로 숫자 할당해서 Map 객체의 요소 개수를 변경할 수 없음

37.2.3 요소 추가

- Map.prototype.set

- 연속적으로 호출 가능

- 중복 값 -> 덮어써짐 /  에러 발생 X

- Map 객체는 키 타입에 제한이 없음

const map = new Map();
map
    .set('key1', 'value1')
    .set('key1', 'value2');
    
console.log(map); // Map(1) {'key1' => 'value2'}

37.2.4 요소 취득

- Map.protoptype.get

- get 메서드의 인수로 키를 전달하면 Map 객체에서 인수로 전달한 키를 갖는 값을 반환함

- 존재하지 않는 경우 undefined 반환

const map = new Map();

const lee = {name:'Lee'};
map.set(lee, 'developer');

console.log(map.get(lee)); // developer

37.2.5 요소 존재 여부 확인

- Map.prototype.has

37.2.6 요소 삭제

- Map.prototype.delete

- 존재하지 않는 키 -> 무시/에러 X

- 연속적으로 호출 불가

const map = new Map();

const lee = {name:'Lee'};
map.set(lee, 'developer');

map.delete(lee);

console.log(map)); // Map(0) {}

37.2.7 요소 일괄 삭제

- Map.prototype.clear

- 언제나 undefined 반환

37.2.8 요소 순회

- Map.prototype.forEach

 

: 첫 번째 인수 - 현재 순회 중인 요소값

: 두 번째 인수 - 현재 순회 중인 요소키

: 세 번째 인수 - 현재 순회 중인 Map 객체 자체

- for ... of 문으로도 순회 가능, 스프레드 문법과 배열 디스트럭처링의 대상이 될 수도 있음

 

const lee = {name : 'Lee'};
const kim = {name : 'Kim'};

const map = new Map([[lee, 'developer'], [kim, 'desinger']]);

map.forEach((v, k, map) => console.log(v, k, map));

- Map 객체는 이터레이터인 객체를 반환하는 메서드 제공

CH42. 비동기 프로그래밍

42.1 동기 처리와 비동기 처리

- JS 엔진은 단 하나의 실행 컨텍스트 스택을 가짐

: 동시에 2개 이상의 함수를 실행할 수 없음 (싱글 스레드)

- 함수는 호출된 순서대로 스택 자료구조인 실행 컨택스트 스택에 푸시되어 실행

 

 

동기 처리

- 다음 예제와 같이 현재 실행 중인 태스크가 종료할 때까지 다음에 실행될 태스크가 대기하는 방식을 동기 처리

- 태스크를 순서대로 하나씩 처리하므로 실행 순서가 보장된다는 장점이 있지만, 

  앞선 태스크가 종료할 때까지 이후 태스크들이 블로킹된다는 단점

function sleep(func, delay){
    const delayUntil = Date.now() + delay;
    
    // 현재 시간에 delay를 더한 delayUntil이 현재 시간보다 작으면 계속 반복
    while (Date.now() < delayUntil);
    
    // 일정 시간이 경과한 이후에 콜백 함수를 호출
    func();
}

function foo() {
    console.log('foo');
}

function bar() {
    console.log('bar');
}

// sleep 함수는 3초 이상 실행됨
sleep(foo, 3*1000);

// bar 함수는 sleep 함수의 실행이 종료된 이후에 호출되므로 3초 이상 블로킹 됨
bar();

 

비동기 처리

fucntion foo() {
    console.log('foo');
}

fucntion bar() {
    console.log('bar');
}

setTimeout(foo, 3*1000);
bar();
// bar호출하고 3초 경과 후 foo 호출

- 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행하는 방식을 비동기 처리

- 현재 실행 중인 태스크가 종료되지 않은 상태라 해도 다음 태스크를 곧바로 실행 -> 블로킹 발생하지 않음

- 태스크의 실행 순서가 보장되지 않는 단점

- 전통적으로 콜백 패턴을 사용함

: 이 콜백 패턴은 콜백 헬을 발생시켜 가독성을 나쁘게 하고, 비동기 처리 중 발생한 에러의 예외 처리가 곤란

: 여러 개의 비동기 처리를 한 번에 처리하는 데도 한계가 있음

42.2 이벤트 루프와 태스크 큐

이벤트 루프

: 콜 스택에 현재 실행 중인 실행 컨택스트가 있는지, 그리고 태스크 큐에 대기 중인 함수가 있는지 반복해서 확인함

: 만약 콜 스택이 비어있어 태스크 큐에 대기 중인 함수가 있다면 이벤트 루프는 순차적으로 태스크 큐에 대기 중인 함수를 콜 스택으로 이동시킴

: 이때 콜 스택으로 이동한 함수는 즉시 실행됨

: 즉, 태스크 큐에 일시 보관된 함수들은 비동기 처리 방식으로 동작함

태스크 큐

: setTimeout이나 setInterval과 같은 비동기 함수의 콜백 함수 또는 이벤트 핸들러가 일시적으로 보관되는 영역

: 태스크 큐와는 별도로 프로미스의 후속 처리 메서드의 콜백 함수가 일시적으로 보관되는 마이크로 태스크 큐도 존재함

 

- JS엔진은 싱글 스레드로 동작하지만, 브라우저는 멀티 스레드로 동작함