-
[자바스크립트 코딩의 기술] 3장Front-end/JavaScript 2020. 7. 6. 23:43
<자바스크립트 코딩의 기술>을 읽고 작성합니다.
3장 특수한 컬렉션을 이용해 코드 명료성을 극대화하라
객체를 이용해 정적인 키-값을 탐색해라
원칙적으로 객체는 변화가 없고 구조화된 키-값 데이터를 다루는 경우에 유용하다.
.
을 통해 직접 참조하거나 배열처럼 접근이 가능하다. 이처럼 객체는 단순하기 때문에 정적인 정보를 다룰 때 훌륭하다.
하지만 계속해서 갱신, 반복, 대체, 정렬해야 하는 정보에는 적절하지 않다.
이런 경우에는 맵을 사용하는 것이 낫다.
단, 기존의 객체를 조작하는 것이 아니라 각각의 함수에서 새로운 객체를 생성하면 조작없이 사용 가능하다.중괄호에 key-value를 작성하는 것을 객체 리터럴
object literal
이라고 한다.비구조화 할당(destructuring) 은 빠른 탐색이 필요할 경우 객체를 선택해야 할 이유 중 하나이다.
Object.assign()으로 조작 없이 객체를 생성하라
ES6에서는 Object.assign()을 새롭게 추가해 다른 객체의 키-값으로 객체의 필드를 생성하고 갱신할 수 있도록 했다.
const defaults = { author: '', title: '', year: 2017, rating: null } const book = { author: 'Joe Morgan', title: 'Simplifying JavaScript' } Object.assign(defaults, book); // 먼저 전달한 객체부터 적용됨
Object.assign을 사용하면 코드는 간결해지지만, 원본 객체를 조작하게 되므로 첫 번째 객체에 빈 객체를 사용한다.
const updated = Object.assign({}, defaults, book);
그렇지만 값이 단순 정수가 아니라 객체인 경우에는 문제가 발생한다.
중첩된 객체가 있는 객체를 복사하는 것을 깊은 복사deep copy
또는 깊은 병합deep merge
이라고 한다.
중첩된 객체를 담고 있는 객체가 가지고 있는 것은 중첩된 객체에 대한 참조이며, 참조에 대한 복사만으로는 중첩된 객체에 깊은 복사를 적용할 수 없다.
(== 단지 참조의 위치 복사, 값 변경 시 원본과 복사 모두 변경)▶️ Object.assign()을 이용해서 중첩된 객체를 복사해야 한다.
ES2018 이후 객체 스프레드 연산자가 명세에 포함되었음
객체 펼침 연산자로 정보를 갱신하라
배열 스프레드 연산자처럼 새로운 데이터를 스프레드 연산자 앞, 뒤에 쉽게 추가할 수 있다.
또한 독립적으로 사용할 수는 없고 객체에 펼쳐지게 해야 한다.
배열 스프레드 연산자와의 차이는 동일한 키에 서로 다른 값을 추가하면 어떤 값이든 가장 마지막에 선언된 값을 사용한다는 것이다.const book = { title : 'Reasons and Persons', author: 'Derek Parfit' } const update = { ...book, title: 'Reasons & Persons' } // { title : 'Reasons & Persons', author: 'Derek Parfit' }
const updated = Object.assign({}, defaults, book); const bookWithDefaults = { ...defaults, ...book }; // 동일
// deepcopy const employee = { ...defaultEmployee, name : { ...defaultEmployee.name, }, }
Map으로 명확하게 키-값 데이터를 갱신하라
참고 : MDN - Map
Map 객체는 키-값 쌍을 저장하며 각 쌍의 삽입 순서도 기억하는 콜렉션으로, 객체와 원시 값 모두 키와 값으로 사용할 수 있다.
요소의 삽입 순서대로 키를 조회한다는 특징이 있다.Map이 Object보다 더 선호되는 상황
- Object는 프로토타입이므로 기본 키가 존재할 수 있으나 Map은 명시한 키 이외의 키를 가지지 않는다.
- Map의 키는 함수, 객체 등을 포함한 모든 값이 가능하지만 Object는 반드시 String 또는 Symbol 이다. ✔️
- Object 키는 정렬되지 않지만 Map의 키는 삽입순으로 정렬된다.
- 항목 수를 size 속성으로 쉽게 알 수 있다.
- Map은 키를 순회하는 것이 바로 가능하지만 Object는 모든 키를 알아낸 후 그 키의 배열을 순회한다.
- 잦은 키-값 쌍의 추가와 제거에서 더 좋은 성능을 보인다. ✔️
✔️ -> 책에서 다루는 부분
let filters = new Map();
중괄호로 생성장를 대신할 수 있는 객체와 달리 맵에서는 항상 명시적으로 새로운 인스턴스를 생성한다.
fitlers.set('견종','래브라도레트리버'); // (key, value)
데이터를 추가할 때는
set()
메소드를 이용해서 추가한다.fitlers.get('견종'); // '래브라도레트리버'
데이터를 가져올 때는
get()
메소드를 사용한다. 파라미터로는 key 만 전달한다.
이렇게 데이터를 가져오고 설정하는 건 귀찮으므로 차례로 연결해서 값을 추가하는체이닝(chaining)
방식을 사용한다.let filters = new Map() .set('견종', '래브라도리트리버') .set('크기', '대형견') .set('색상', '갈색'); filters.get('크기'); // 대형견
let filters = new Map() [ ['견종', '래브라도리트리버'], ['크기', '대형견'], ['색상', '갈색'] ] filters.get('색상'); // 갈색 filters.delete('색상'); // 값 삭제 filters.get('색상'); // undefined filters.clear(); // 모든 키-값 삭제
// Object let filters = {}; function addFilters(filters, key, value) { filters[key] = value; } function deleteFilters(filters, key) { delete filters[key]; } function clearFilters(filters) { filters = {}; return filters; } // Map const petFilters = new Map(); function addFilters(filters, key, value) { filters.set(key, value)l } function deleteFilters(filters, key) { filters.delete(key); } function clearFilters(filters) { filters.clear(); }
⚡️ 정보를 자주 변경하는 경우에는 맵을 사용하는 것이 훨씬 편리함! 모든 동작과 의도가 매우 명료함
⚡️ 키에 사용할 수 있는 자료형에 제약이 없음, 정수를 객체의 키 값으로 사용한다면 문자열로 변경됨const errors = { 100: '이름이 잘못되었습니다.', 110: '이름에는 문자만 입력할 수 있습니다.', 200: '색상이 잘못되었습니다.' }; Object.keys(errors); // ['100','110','200'] const errorsMap = new Map([ [100, '이름이 잘못되었습니다.'] [110, '이름에는 문자만 입력할 수 있습니다.'] [200, '색상이 잘못되었습니다.'] ]); errors.keys(); // MapIterator { 100, 110, 200 }
맵과 펼침 연산자로 키-값 데이터를 순회하라
객체는 순회하기가 번거롭다. 그나마
for ...in
문을 사용할 수 있지만 키 외에는 접근할 수 없다.const filters = { color: 'black', breed: 'Retriever' }; function getAppliedFilters(filters) { const keys = Object.keys(filters); const applied = []; for (const key of keys) { applied.push(`${key}:${filters[key]}`); } return `선택한 조건은 ${applied.join(',')} 입니다.`; }
복잡하게 순회를 해야 하며 순서가 보장되지 않는다 는 단점이 있다. 보장하려면 먼저 정렬을 해야 한다.
const filters = new Map() .set('color', 'black') .set('breed', 'Retriever') filters.entries(); // MapIterator { ['color', 'black'], ['breed', 'Retriever']} function getAppliedMapFilters(filters) { const applied = []; for (const [key, value] of keys) { applied.push(`${key}:${value}`); } return `선택한 조건은 ${applied.join(',')} 입니다.`; }
ES2017부터 공식적으로 명세에 Object의 내장 메소드 Object.entries()로 추가되었음
Map에서도 스프레드 연산자를 사용할 수 있다. 배열은 단일 값만 반환되지만 Map은 키-값 쌍이 반환된다.
console.log(...filters); // ['color', 'black'], ['breed', 'Retriever']
Map은 정렬 메소드가 맵에는 내장되어 있지 않지만 명시적으로 비교함수를 제공하여 정렬을 할 수 있다.
function sortByKey(a, b) { return a[0] > b[0]? 1 : -1; } function getAppliedMapFilters(filters) { const applied = []; for (const [key, value] of [...filters].sort(sortByKey)) { applied.push(`${key}:${value}`); } return `선택한 조건은 ${applied.join(',')} 입니다.`; }
맵 생성 시 부수효과(side effect)를 피하라
디폴트 값이 있고 새로운 값으로 업데이트를 할 때 발생할 수 있는 사이드 이펙트를 피해야 한다.
function applyDefaults(map, defaults) { return new Map([...defaults, ...map]); }
Set을 이용해 고윳값을 관리하라
참고 : MDN - Set
Set 객체는 값 콜렉션으로, 삽입 순서대로 요소를 순회할 수 있다. 하나의 Set 내 값은 한 번만 나타날 수 있다.
즉, 어떤 값은 그 Set 콜렉션 내에서 유일하다는 특징이 있다.const dogs = [ ... ] // 개에 대한 객체들이 담겨있는 배열 function getColors(dogs) { retrun dogs.map(dog => dog['color']); } getColors(dogs); // ['black','black','brown'...]
중복된 값들이 존재하는 배열이 있을 때 고유한 값들만 골라내려면?
// 방법 1 function getUnique(attributes) { const unique = []; for (const attribute of attributes) { if (!unique.includes(attribute)) { unique.push(attribute); } } return unique; } // 방법 2 function getUnique(attributes) { return [...new Set(attributes)] } // 방법 3 function getUniqueColors(dogs) { const unique = new Set(); for (const dog of dogs){ unique.add(dog.color); } return [...unique]; } // 방법 4 reduce 이용 [...dogs.reduce((colors, color) => colors.add(color), new Set())];
'Front-end > JavaScript' 카테고리의 다른 글
[자바스크립트 코딩의 기술] 5장 (0) 2020.07.06 [자바스크립트 코딩의 기술] 4장 (0) 2020.07.06 [자바스크립트 코딩의 기술] 2장 (0) 2020.07.02 [자바스크립트 코딩의 기술] 1장 (0) 2020.07.02 특정 날짜의 요일 구하기, 가격 컴마 표기 (0) 2020.05.15