8일차 - 2022.03.08

안병욱·2022년 3월 8일
0

오늘 공부한 내용 요약

( 모던 JavaScript 튜토리얼 학습 )

1. 맵과 셋

객체 – 키가 있는 컬렉션을 저장함
배열 – 순서가 있는 컬렉션을 저장함

하지만 현실을 반영하기엔 이 두 자료구조 만으론 부족해서 맵과 셋이 등장하게 되었습니다.


  • Map

키가 있는 데이터를 저장한다는 점은 객체와 유사하지만 맵은 키에 다양한 자료형을 허용한다는 점에서 차이가 있습니다.

new Map() – 맵을 만듭니다.
map.set(key, value) – key를 이용해 value를 저장합니다.
map.get(key) – key에 해당하는 값을 반환합니다. key가 존재하지 않으면 undefined를 반환합니다.
map.has(key) – key가 존재하면 true, 존재하지 않으면 false를 반환합니다.
map.delete(key) – key에 해당하는 값을 삭제합니다.
map.clear() – 맵 안의 모든 요소를 제거합니다.
map.size – 요소의 개수를 반환합니다.

예시

let map = new Map();

map.set('1', 'str1');   // 문자형 키
map.set(1, 'num1');     // 숫자형 키
map.set(true, 'bool1'); // 불린형 키

// 객체는 키를 문자형으로 변환한다는 걸 기억하고 계신가요?
// 맵은 키의 타입을 변환시키지 않고 그대로 유지합니다. 따라서 아래의 코드는 출력되는 값이 다릅니다.
alert( map.get(1)   ); // 'num1'
alert( map.get('1') ); // 'str1'

alert( map.size ); // 3

객체를 키로 사용할 수 있다는 점은 맵의 가장 중요한 기능 중 하나입니다. 객체에는 문자열 키를 사용할 수 있습니다. 하지만 객체 키는 사용할 수 없습니다.

let john = { name: "John" };

let visitsCountObj = {}; // 객체를 하나 만듭니다.

visitsCountObj[john] = 123; // 객체(john)를 키로 해서 객체에 값(123)을 저장해봅시다.

// 원하는 값(123)을 얻으려면 아래와 같이 키가 들어갈 자리에 `object Object`를 써줘야합니다.
alert( visitsCountObj["[object Object]"] ); // 123

->visitsCountObj는 객체이기 때문에 모든 키를 문자형으로 변환시킵니다. 이 과정에서 john은 문자형으로 변환되어 "[object Object]"가 됩니다.



  • 맵의 요소에 반복 작업하기

map.keys() – 각 요소의 키를 모은 반복 가능한 객체를 반환합니다.
map.values() – 각 요소의 값을 모은 이터러블 객체를 반환합니다.
map.entries() – 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환합니다

let recipeMap = new Map([
  ['cucumber', 500],
  ['tomatoes', 350],
  ['onion',    50]
]);

for (let vegetable of recipeMap.keys()) {
  alert(vegetable); // cucumber, tomatoes, onion
}

for (let amount of recipeMap.values()) {
  alert(amount); // 500, 350, 50
}

for (let entry of recipeMap) { // recipeMap.entries()와 동일합니다.
  alert(entry); // cucumber,500 ...
}

맵은 배열과 유사하게 내장 메서드 forEach도 지원합니다.


  • Object.entries: 객체를 맵으로 바꾸기

각 요소가 키-값 쌍인 배열이나 이터러블 객체를 초기화 용도로 맵에 전달해 새로운 맵을 만들 수 있습니다.

let map = new Map([
  ['1',  'str1'],
  [1,    'num1'],
  [true, 'bool1']
]);

alert( map.get('1') ); // str1

평범한 객체를 가지고 맵을 만들려면 내장 메서드 Object.entries(obj)를 활용

let obj = {
  name: "John",
  age: 30
};

let map = new Map(Object.entries(obj));

alert( map.get('name') ); // John


  • Object.FromEntries : 맵을 객체로 바꾸기
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);

let obj = Object.fromEntries(map.entries()); // 맵을 일반 객체로 변환 (*)

// 맵이 객체가 되었습니다!
// obj = { banana: 1, orange: 2, meat: 4 }

alert(obj.orange); // 2

map.entries()를 호출하면 맵의 [키, 값]을 요소로 가지는 이터러블을 반환합니다





  • 셋(set)
    중복을 허용하지 않는 값을 모아놓음. 셋에 키가 없는 값이 저장됨

셋 내에 동일한 값이 있다면 set.add(value)을 아무리 많이 호출하더라도 아무런 반응이 없다

new Set(iterable) – 셋을 만듭니다. 이터러블 객체를 전달받으면 그 안의 값을 복사해 셋에 넣어줍니다.
set.add(value) – 값을 추가하고 셋 자신을 반환합니다.
set.delete(value) – 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.
set.clear() – 셋을 비웁니다.
set.size – 셋에 몇 개의 값이 있는지 세줍니다.


  • 셋의 값에 반복 작업하기
    for..of나 forEach를 사용하면 셋의 값을 대상으로 반복 작업을 수행가능
let set = new Set(["oranges", "apples", "bananas"]);

for (let value of set) alert(value);

// forEach를 사용해도 동일하게 동작합니다.
set.forEach((value, valueAgain, set) => {
  alert(value);
});

set.keys() – 셋 내의 모든 값을 포함하는 이터러블 객체를 반환합니다.
set.values() – set.keys와 동일한 작업을 합니다. 맵과의 호환성을 위해 만들어진 메서드입니다.
set.entries() – 셋 내의 각 값을 이용해 만든 [value, value] 배열을 포함하는 이터러블 객체를 반환합니다. 맵과의 호환성을 위해 만들어졌습니다.




2. 위크맵과 위크셋

배열에 객체 하나를 추가시 배열이 메모리에 남아있는 한, 이 객체를 참조하는 것이 없더라도 배열의 요소인 이 객체도 메모리에 남아있게 된다.
객체의 프로퍼티나 배열의 요소, 맵이나 셋을 구성하는 요소들이 이에 해당함
위크맵을 사용하면 키로 쓰인 객체가 가비지 컬렉션의 대상이 됩니다.


* 위크맵

위크맵과 맵의 차이점
1. 위크맵의 키가 반드시 객체여야 한다는 점 원시값은 위크맵의 키가 될 수 없습니다.

let john = { name: "John" };

let weakMap = new WeakMap();
weakMap.set(john, "...");

john = null; // 참조를 덮어씀

// john을 나타내는 객체는 이제 메모리에서 지워집니다!

2.위크맵은 반복 작업과 keys(), values(), entries() 메서드를 지원하지 않는다. 따라서 위크맵에선 키나 값 전체를 얻는 게 불가능합니다.

위크맵은
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
의 메서드만 지원하는데 이는 가비지 컬렉션의 동작방식 때문이다.
->
가비지 컬렉션이 일어나는 시점은 자바스크립트 엔진이 결정한다. 객체가 모든 참조를 잃었을 때, 바로 메모리에서 삭제될 수도 있고, 다른 삭제 작업이 있을 때까지 대기하다가 함께 삭제될 수도 있다. 현재 위크맵에 요소가 몇 개 있는지 정확히 파악하는 것 자체가 불가능한 것이죠. 가비지 컬렉터가 한 번에 메모리를 청소할 수도 있고, 부분 부분 메모리를 청소할 수도 있으므로 위크맵의 요소 전체를 대상으로 무언가를 하는 메서드는 동작 자체가 불가능합니다.



  • 유스 케이스 : 추가데이터
    위크맵의 진가는 부차적인 데이터를 저장할 곳이 필요할때 발휘된다.

객체에 데이터를 추가해줄때, 추가해줄 데이터는 객체가 살아있을때만 유요한데 이때 위크맵에 저장한다.
객체가 가비지 컬렉션의 대상이 될 때, 데이터도 함께 사라지게 됩니다.

ex) 맵 요소의 키에 특정사용자정보, 값에 방문횟수를 저장하는 경우
사용자의 정보가 필요없는 상황이 오면 방문횟수 저장도 필요 없어짐

let visitsCountMap = new Map(); 

function countUser(user) {
  let count = visitsCountMap.get(user) || 0;
  visitsCountMap.set(user, count + 1);
}

아래는 John이라는 사용자가 방문했을 때, 어떻게 방문 횟수가 증가하는지를 보여줍니다.

let john = { name: "John" };

countUser(john); // John의 방문 횟수를 증가시킵니다.

// John의 방문 횟수를 셀 필요가 없어지면 아래와 같이 john을 null로 덮어씁니다.
john = null;

특정 사용자를 나타내는 객체가 메모리에서 사라지면 해당 객체에 대한 정보도 우리가 지워야한다. 그렇지 않으면 visitsCountMap가 차지하는 메모리 공간이 한없이 커질 겁니다. 애플리케이션 구조가 복잡할 땐, 이렇게 쓸모 없는 데이터를 수동으로 비워주는 게 꽤 골치 아픕니다.

이를 위크맵이 예방 가능

let visitsCountMap = new WeakMap(); // 위크맵에 사용자의 방문 횟수를 저장함

// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
  let count = visitsCountMap.get(user) || 0;
  visitsCountMap.set(user, count + 1);
}

위크맵을 사용해 사용자 방문 횟수를 저장하면 visitsCountMap을 수동으로 청소해줄 필요가 없습니다. john을 나타내는 객체가 도달 가능하지 않은 상태가 되면 자동으로 메모리에서 삭제되기 때문입니다.



  • 유스케이스 : 캐싱
    위크맵은 캐싱(caching)이 필요할 때도 유용
    캐싱 - 시간이 오래 걸리는 작업의 결과를 저장해 연산시간과 비용을 절약해주는 기법
let cache = new WeakMap();

function process(obj) {
  if (!cache.has(obj)) {
    let result = /* 연산 수행 */ obj;

    cache.set(obj, result);
  }

  return cache.get(obj);
}


let obj = {/* ... 객체 ... */};

let result1 = process(obj);
let result2 = process(obj);

// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;

// obj가 가비지 컬렉션의 대상이 되므로, 캐싱된 데이터 역시 메모리에서 삭제
// 삭제가 진행되면 cache엔 그 어떤 요소도 남아있지 않을겁니다.


  • 위크셋

-위크셋은 셋과 유사하지만 객체만 저장할 수 있다는 점이 다릅니다. 원시값은 저장할 수 없습니다.
-셋 안의 객체는 도달 가능할 때만 메모리에서 유지됩니다.
-셋과 마찬가지로 위크셋이 지원하는 메서드는 단출합니다. add, has, delete를 사용할 수 있고, size, keys()나 반복 작업 관련 메서드는 사용할 수 없습니다.
-위크셋도 부차적인 데이터를 저장할때 사용할 수 있다
다만 복잡한 데이터가 아닌 true,false 같은 간단한 답변을 얻는 용도로 사용

위크맵과 위크셋의 가장 큰 단점은 반복 작업이 불가능하다는 점이다.


  • 요약

위크맵을 구성하는 요소의 키는 오직 객체만 가능합니다. 키로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값 역시 삭제됩니다.

위크셋엔 객체만 저장할 수 있습니다. 위크셋에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제됩니다.

두 자료구조 모두 구성 요소 전체를 대상으로 하는 메서드를 지원하지 않습니다. 구성 요소 하나를 대상으로 하는 메서드만 지원합니다.




3. Object.keys, values, entries

  • keys(), values(), entries()를 사용할 수 있는 자료구조는
    Map, Set, Array이다

  • Map, Set, Array 전용 메서드와 일반 객체용 메서드의 차이를 비교
  1. obj.keys()가 아닌 Object.keys(obj)를 호출한다는 점
  2. 메서드 Object.*를 호출하면 iterable 객체가 아닌 배열을 반환한다는 점


  • 객체 변환하기

객체엔 배열전용 메서드를 적용할 수 없다.
Object.entries와 Object.fromEntries를 순차 적용하면 가능한데

  1. 그대로는 배열전용 메서드를 쓸수 없으니 Object.entries를 이용해 배열을 반환하고
  2. 전용 메서드를 적용한뒤
  3. Object.fromEntries을 사용해 객체로 다시 돌리는 방법


4. 구조 분해 할당

  • 배열 분해하기
let arr = ["Bora", "Lee"]

let [firstName, surname] = arr;

alert(firstName); // Bora
alert(surname);  // Lee

배열을 사용하지 않고도 변수로 분해함.
이외에도 split을 이용해

let [firstName, surname] = "Bora Lee".split(' ');  

요렇게도 가능



let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert( title ); // Consul

쉼표를 이용해 특정 배열 요소를 제거도 가능하다.



let guest = "Jane";
let admin = "Pete";

[guest, admin] = [admin, guest];

변수 교환도 가능



  • 나머지 요소 가져오기 (...)
let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];

alert(name1); // Julius
alert(rest[0]); // Consul

rest외에 다른 이름을 사용해도 무방.
변수 마지막 위치와 ...을 사용하는 것은 지켜야함



* 기본값

할당 값이 없으면 에러가 아니라 undefined
할당값이 없을때 '='를 이용해 직접 기본값 설정 가능

et [name = "Guest", surname = "Anonymous"] = ["Julius"];

alert(name);    // Julius (배열에서 받아온 값)

  • 객체 분해하기
    let {var1, var2} = {var1:…, var2:…} 형식
let {height, width, title} = { title: "Menu", height: 200, width: 100 } 

다른 이름을 가진 변수에 저장할때 :를 이용

let options = {
  title: "Menu”,
  height: 100
};

let {height: w, title} = options;

alert(title);  // Menu
alert(w);      // 100
----------------------------------------------------------------
let { title } = options;
									필요한 부분만 반환도 가능 
alert(title); // Menu

* let 없이 사용

자바스크립트가 코드 블록이 아닌 표현식으로 인식하도록 ( ) 로 감사주기

let title, width, height;

// 에러가 발생하지 않습니다.
({title, width, height} = {title: "Menu", width: 200, height: 100});

alert( title ); // Menu


  • 중첩 구조 분해
    객체나 배열이 다른 객체나 배열을 포함하는 경우, 좀 더 복잡한 패턴을 사용하면 중첩 배열이나 객체의 정보를 추출가능
let options = {
  size: {
    width: 100,
  },
  items: ["Cake", "Donut"];
  extra : true;
};

let {
  size: { 
    width,
  },
  items: [item1, item2], 
  title = "Menu" 
} = options;

alert(title);  // Menu
alert(width);  // 100
alert(item1);  // Cake
alert(item2);  // Donut

공부사이트

코어 자바스크립트


위의 내용은 공부중 본인이 이해한 내용으로 몇몇 틀린 내용이 있을 수 있습니다.
회독중 발견시 수정하겠습니다

profile
working hard

0개의 댓글