오늘 공부한 내용 요약
( 모던 JavaScript 튜토리얼 학습 )
객체 – 키가 있는 컬렉션을 저장함
배열 – 순서가 있는 컬렉션을 저장함
하지만 현실을 반영하기엔 이 두 자료구조 만으론 부족해서 맵과 셋이 등장하게 되었습니다.
키가 있는 데이터를 저장한다는 점은 객체와 유사하지만 맵은 키에 다양한 자료형을 허용한다는 점에서 차이가 있습니다.
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도 지원합니다.
각 요소가 키-값 쌍인 배열이나 이터러블 객체를 초기화 용도로 맵에 전달해 새로운 맵을 만들 수 있습니다.
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
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.add(value)을 아무리 많이 호출하더라도 아무런 반응이 없다
new Set(iterable) – 셋을 만듭니다. 이터러블 객체를 전달받으면 그 안의 값을 복사해 셋에 넣어줍니다.
set.add(value) – 값을 추가하고 셋 자신을 반환합니다.
set.delete(value) – 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.
set.clear() – 셋을 비웁니다.
set.size – 셋에 몇 개의 값이 있는지 세줍니다.
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] 배열을 포함하는 이터러블 객체를 반환합니다. 맵과의 호환성을 위해 만들어졌습니다.
배열에 객체 하나를 추가시 배열이 메모리에 남아있는 한, 이 객체를 참조하는 것이 없더라도 배열의 요소인 이 객체도 메모리에 남아있게 된다.
객체의 프로퍼티나 배열의 요소, 맵이나 셋을 구성하는 요소들이 이에 해당함
위크맵을 사용하면 키로 쓰인 객체가 가비지 컬렉션의 대상이 됩니다.
위크맵과 맵의 차이점
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을 나타내는 객체가 도달 가능하지 않은 상태가 되면 자동으로 메모리에서 삭제되기 때문입니다.
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 같은 간단한 답변을 얻는 용도로 사용
위크맵과 위크셋의 가장 큰 단점은 반복 작업이 불가능하다는 점이다.
위크맵을 구성하는 요소의 키는 오직 객체만 가능합니다. 키로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값 역시 삭제됩니다.
위크셋엔 객체만 저장할 수 있습니다. 위크셋에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제됩니다.
두 자료구조 모두 구성 요소 전체를 대상으로 하는 메서드를 지원하지 않습니다. 구성 요소 하나를 대상으로 하는 메서드만 지원합니다.
객체엔 배열전용 메서드를 적용할 수 없다.
Object.entries와 Object.fromEntries를 순차 적용하면 가능한데
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 {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 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
공부사이트
위의 내용은 공부중 본인이 이해한 내용으로 몇몇 틀린 내용이 있을 수 있습니다.
회독중 발견시 수정하겠습니다