요약
맵은 키가 있는 값이 저장된 컬렉션입니다.
주요 메서드와 프로퍼티:
- new Map([iterable]) – 맵을 만듭니다. [key,value]쌍이 있는 iterable(예: 배열)을 선택적으로 넘길 수 있는데, 이때 넘긴 이터러블 객체는 맵 초기화에 사용됩니다.
- map.set(key, value) – 키를 이용해 값을 저장합니다.
- map.get(key) – 키에 해당하는 값을 반환합니다. key가 존재하지 않으면 undefined를 반환합니다.
- map.has(key) – 키가 있으면 true, 없으면 false를 반환합니다.
- map.delete(key) – 키에 해당하는 값을 삭제합니다.
- map.clear() – 맵 안의 모든 요소를 제거합니다.
- map.size – 요소의 개수를 반환합니다.
일반적인 객체와의 차이점:
- 키의 타입에 제약이 없습니다. 객체도 키가 될 수 있습니다.
- size 프로퍼티 등의 유용한 메서드나 프로퍼티가 있습니다.
셋은 중복이 없는 값을 저장할 때 쓰이는 컬렉션입니다.
주요 메서드와 프로퍼티:
- new Set([iterable]) – 셋을 만듭니다. iterable 객체를 선택적으로 전달받을 수 있는데(대개 배열을 전달받음), 이터러블 객체 안의 요소는 셋을 초기화하는데 쓰입니다.
- set.add(value) – 값을 추가하고 셋 자신을 반환합니다. 셋 내에 이미 value가 있는 경우 아무런 작업을 하지 않습니다.
- set.delete(value) – 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true, 아니면 false를 반환합니다.
- set.has(value) – 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.
- set.clear() – 셋을 비웁니다.
- set.size – 셋에 몇 개의 값이 있는지 세줍니다.
맵과 셋에 반복 작업을 할 땐, 해당 컬렉션에 요소나 값을 추가한 순서대로 반복 작업이 수행됩니다. 따라서 이 두 컬렉션은 정렬이 되어있지 않다고 할 수 없습니다. 그렇지만 컬렉션 내 요소나 값을 재 정렬하거나 (배열에서 인덱스를 이용해 요소를 가져오는 것처럼) 숫자를 이용해 특정 요소나 값을 가지고 오는 것은 불가능합니다.
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
map[key]는 Map을 쓰는 바른 방법이 아닙니다.
- map[key] = 2로 값을 설정하는 것 같이 map[key]를 사용할 수 있긴 합니다. 하지만 이 방법은 map을 일반 객체처럼 취급하게 됩니다. 따라서 여러 제약이 생기게 되죠.
- map을 사용할 땐 map전용 메서드 set, get 등을 사용해야만 합니다.
let john = { name: "John" };
// 고객의 가게 방문 횟수를 세본다고 가정해 봅시다.
let visitsCountMap = new Map();
// john을 맵의 키로 사용하겠습니다.
visitsCountMap.set(john, 123);
alert( visitsCountMap.get(john) ); // 123
[object Object]
가 됨map.set
을 호출할 때마다 맵 자신이 반환되기 때문에 map.set
을 '체이닝(chaining)'할 수 있습니다.map.set('1', 'str1').set(1, 'num1').set(true, 'bool1');
map.keys()
– 각 요소의 키를 모은 반복 가능한(iterable, 이터러블) 객체를 반환합니다.map.values()
– 각 요소의 값을 모은 이터러블 객체를 반환합니다.map.entries()
– 요소의 [키, 값]을 한 쌍으로 하는 이터러블 객체를 반환합니다. 이 이터러블 객체는 for..of
반복문의 기초로 쓰입니다.forEach
– 맵은 배열과 유사하게 내장 메서드 forEach도 지원합니다.let recipeMap = new Map([
['cucumber', 500],
['tomatoes', 350],
['onion', 50]
]);
// 키(vegetable)를 대상으로 순회합니다.
for (let vegetable of recipeMap.keys()) {
alert(vegetable); // cucumber, tomatoes, onion
}
// 값(amount)을 대상으로 순회합니다.
for (let amount of recipeMap.values()) {
alert(amount); // 500, 350, 50
}
// [키, 값] 쌍을 대상으로 순회합니다.
for (let entry of recipeMap) { // recipeMap.entries()와 동일합니다.
alert(entry); // cucumber,500 ...
}
맵은 삽입 순서를 기억합니다.
맵은 값이 삽입된 순서대로 순회를 실시합니다. 객체가 프로퍼티 순서를 기억하지 못하는 것과는 다릅니다.
let map = new Map([
['1', 'str1'],
[1, 'num1'],
[true, 'bool1']
]);
alert( map.get('1') ); // str1
Object.entries(obj)
: 객체의 키-값 쌍을 요소([key, value])로 가지는 배열을 반환let obj = {
name: "John",
age: 30
};
let map = new Map(Object.entries(obj));
alert( map.get('name') ); // John
Object.fromEntries
: 각 요소가 [키, 값] 쌍인 배열을 객체로 바꿔줌let prices = Object.fromEntries([
['banana', 1],
['orange', 2],
['meat', 4]
]);
// now prices = { banana: 1, orange: 2, meat: 4 }
alert(prices.orange); // 2
let map = new Map();
map.set('banana', 1);
map.set('orange', 2);
map.set('meat', 4);
//let obj = Object.fromEntries(map.entries());
let obj = Object.fromEntries(map); // .entries()를 생략가능
// obj = { banana: 1, orange: 2, meat: 4 }
alert(obj.orange); // 2
new Set(iterable)
– 셋을 만듭니다. 이터러블 객체를 전달받으면(대개 배열을 전달받음) 그 안의 값을 복사해 셋에 넣어줍니다.set.add(value)
– 값을 추가하고 셋 자신을 반환합니다.set.delete(value)
– 값을 제거합니다. 호출 시점에 셋 내에 값이 있어서 제거에 성공하면 true
, 아니면 false
를 반환합니다.set.has(value)
– 셋 내에 값이 존재하면 true, 아니면 false를 반환합니다.set.clear()
– 셋을 비웁니다.set.size
– 셋에 몇 개의 값이 있는지 세줍니다.let set = new Set();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
// 어떤 고객(john, mary)은 여러 번 방문할 수 있습니다.
set.add(john);
set.add(pete);
set.add(mary);
set.add(john);
set.add(mary);
// 셋에는 유일무이한 값만 저장됩니다.
alert( set.size ); // 3
for (let user of set) {
alert(user.name); // // John, Pete, Mary 순으로 출력됩니다.
}
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] 배열을 포함하는 이터러블 객체를 반환합니다. 맵과의 호환성을 위해 만들어졌습니다.요약
위크맵은 맵과 유사한 컬렉션입니다. 위크맵을 구성하는 요소의 키는 오직 객체만 가능합니다. 키로 사용된 객체가 메모리에서 삭제되면 이에 대응하는 값 역시 삭제됩니다.위크셋은 셋과 유사한 컬렉션입니다. 위크셋엔 객체만 저장할 수 있습니다. 위크셋에 저장된 객체가 도달 불가능한 상태가 되면 해당 객체는 메모리에서 삭제됩니다.
두 자료구조 모두 구성 요소 전체를 대상으로 하는 메서드를 지원하지 않습니다. 구성 요소 하나를 대상으로 하는 메서드만 지원합니다.
객체엔 ‘주요’ 자료를, 위크맵과 위크셋엔 ‘부수적인’ 자료를 저장하는 형태로 위크맵과 위크셋을 활용할 수 있습니다. 객체가 메모리에서 삭제되면, (그리고 오로지 위크맵과 위크셋의 키만 해당 객체를 참조하고 있다면) 위크맵이나 위크셋에 저장된 연관 자료들 역시 메모리에서 자동으로 삭제됩니다.
위크맵의 키가 반드시 객체여야 함
(원시값은 위크맵의 키가 될 수 없음)
위크맵의 키로 사용된 객체를 참조하는 것이 아무것도 없다면 해당 객체는 메모리와 위크맵에서 자동으로 삭제됨
위크맵은 반복 작업과 keys()
, values()
, entries()
메서드를 지원하지 않음
weakMap.get(key)
weakMap.set(key, value)
weakMap.delete(key)
weakMap.has(key)
weakMap.set(john, "비밀문서");
// john이 사망하면, 비밀문서는 자동으로 파기됩니다.
예시 :
// 📁 visitsCount.js
let visitsCountMap = new WeakMap(); // 위크맵에 사용자의 방문 횟수를 저장함
// 사용자가 방문하면 방문 횟수를 늘려줍니다.
function countUser(user) {
let count = visitsCountMap.get(user) || 0;
visitsCountMap.set(user, count + 1);
}
// 📁 main.js
let john = { name: "John" };
countUser(john); // John의 방문 횟수를 증가시킵니다.
// John의 방문 횟수를 셀 필요가 없어지면 아래와 같이 john을 null로 덮어씁니다.
john = null;
위크맵은 캐싱(caching)이 필요할 때 유용함
캐싱은 시간이 오래 걸리는 작업의 결과를 저장해서 연산 시간과 비용을 절약해주는 기법
동일한 함수를 여러 번 호출해야 할 때, 최초 호출 시 반환된 값을 어딘가에 저장해 놓았다가 그다음엔 함수를 호출하는 대신 저장된 값을 사용하는 게 캐싱의 실례임
예시: Map
활용
// 📁 cache.js
let cache = new Map();
// 연산을 수행하고 그 결과를 맵에 저장합니다.
function process(obj) {
if (!cache.has(obj)) {
let result = /* 연산 수행 */ obj;
cache.set(obj, result);
}
return cache.get(obj);
}
// 함수 process()를 호출해봅시다.
// 📁 main.js
let obj = {/* ... 객체 ... */};
let result1 = process(obj); // 함수를 호출합니다.
// 동일한 함수를 두 번째 호출할 땐,
let result2 = process(obj); // 연산을 수행할 필요 없이 맵에 저장된 결과를 가져오면 됩니다.
// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;
alert(cache.size); // 1 (엇! 그런데 객체가 여전히 cache에 남아있네요. 메모리가 낭비되고 있습니다.)
WeakMap
활용// 📁 cache.js
let cache = new WeakMap();
// 연산을 수행하고 그 결과를 위크맵에 저장합니다.
function process(obj) {
if (!cache.has(obj)) {
let result = /* 연산 수행 */ obj;
cache.set(obj, result);
}
return cache.get(obj);
}
// 📁 main.js
let obj = {/* ... 객체 ... */};
let result1 = process(obj);
let result2 = process(obj);
// 객체가 쓸모없어지면 아래와 같이 null로 덮어씁니다.
obj = null;
// 이 예시에선 맵을 사용한 예시처럼 cache.size를 사용할 수 없습니다.
// 하지만 obj가 가비지 컬렉션의 대상이 되므로, 캐싱된 데이터 역시 메모리에서 삭제될 겁니다.
// 삭제가 진행되면 cache엔 그 어떤 요소도 남아있지 않을겁니다.
let visitedSet = new WeakSet();
let john = { name: "John" };
let pete = { name: "Pete" };
let mary = { name: "Mary" };
visitedSet.add(john); // John이 사이트를 방문합니다.
visitedSet.add(pete); // 이어서 Pete가 사이트를 방문합니다.
visitedSet.add(john); // 이어서 John이 다시 사이트를 방문합니다.
// visitedSet엔 두 명의 사용자가 저장될 겁니다.
// John의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(john)); // true
// Mary의 방문 여부를 확인해보겠습니다.
alert(visitedSet.has(mary)); // false
john = null;
// visitedSet에서 john을 나타내는 객체가 자동으로 삭제됩니다.
위크맵과 위크셋의 가장 큰 단점은 반복 작업이 불가능하다는 점입니다. 위크맵이나 위크셋에 저장된 자료를 한 번에 얻는 게 불가능하죠. 이런 단점은 불편함을 초래하는 것 같아 보이지만, 위크맵과 위크셋을 이용해 할 수 있는 주요 작업을 방해하진 않습니다. 위크맵과 위크셋은 객체와 함께 ‘추가’ 데이터를 저장하는 용도로 쓸 수 있습니다.
keys()
, values()
, entries()
를 사용할 수 있는 자료구조 Map
, Set
, Array
Object.keys(obj)
– 객체의 키만 담은 배열을 반환Object.values(obj)
– 객체의 값만 담은 배열을 반환Object.entries(obj)
– [키, 값] 쌍을 담은 배열을 반환구분 | Map, Set, Array(전용 메서드) | 객체(일반 메서드) |
---|---|---|
호출 문법 | map.keys() | Object.keys(obj)(obj.keys() 아님) |
반환 값 | iterable 객체 | ‘진짜’ 배열 |
obj.keys()
가 아닌 Object.keys(obj)
를 호출
이렇게 문법이 다른 이유는 유연성 때문입니다. 아시다시피 자바스크립트에선 복잡한 자료구조 전체가 객체에 기반합니다. 그러다 보니 객체 data
에 자체적으로 data.values()
라는 메서드를 구현해 사용하는 경우가 있을 수 있습니다. 이렇게 커스텀 메서드를 구현한 상태라도 Object.values(data)
같은 형태로 메서드를 호출할 수 있으면 커스텀 메서드와 내장 메서드 둘 다를 사용할 수 있습니다.
메서드 Object.*를
호출하면 iterable
객체가 아닌 객체의 한 종류인 배열을 반환함
‘진짜’ 배열을 반환하는 이유는 하위 호환성 때문입니다.
Object.keys, values, entries는 심볼형 프로퍼티를 무시함
#객체에 map 사용하기
객체에 전용 메서드 사용하기
map
, filter
같은 배열 전용 메서드를 사용할 수 없지만 Object.entries
와 Object.fromEntries
를 순차적으로 적용하면 객체에도 배열 전용 메서드 사용할 수 있음Object.entries(obj)
를 사용해 객체의 키-값 쌍이 요소인 배열을 얻습니다.map
등의 배열 전용 메서드를 적용Object.fromEntries(array)
를 적용해 배열을 다시 객체로 되돌림let prices = {
banana: 1,
orange: 2,
meat: 4,
};
let doublePrices = Object.fromEntries(
// 객체를 배열로 변환해서 배열 전용 메서드인 map을 적용하고 fromEntries를 사용해 배열을 다시 객체로 되돌립니다.
Object.entries(prices).map(([key, value]) => [key, value * 2])
);
alert(doublePrices.meat); // 8
요약
구조 분해 할당을 사용하면 객체나 배열을 변수로 연결할 수 있습니다.
배열 분해하기:
let [item1 = default, item2, ...rest] = array
array
의 첫 번째 요소는item1
에, 두 번째 요소는 변수item2
에 할당되고, 이어지는 나머지 요소들은 배열rest
저장됩니다.객체 분해하기:
let {prop : varName = default, ...rest} = object
object
의 프로퍼티prop
의 값은 변수varName
에 할당되는데,object
에prop
이 없으면default
가varName
에 할당됩니다.
연결할 변수가 없는 나머지 프로퍼티들은 객체 rest에 복사됩니다.할당 연산자 좌측의 패턴과 우측의 구조가 같으면 중첩 배열이나 객체가 있는 복잡한 구조에서도 원하는 데이터를 뽑아낼 수 있습니다.
// 이름과 성을 요소로 가진 배열
let arr = ["Bora", "Lee"]
// let firstName = arr[0];
// let surname = arr[1];
// 구조 분해 할당을 이용
let [firstName, surname] = arr;
alert(firstName); // Bora
alert(surname); // Lee
'분해(destructuring)'는 '파괴(destructive)'를 의미하지 않습니다.
쉼표를 사용하여 요소 무시하기
// 두 번째 요소는 필요하지 않음 let [firstName, , title] = ["Julius", "Caesar", "Consul", "of the Roman Republic"]; alert( title ); // Consul
할당 연산자 우측엔 모든 이터러블이 올 수 있습니다.
let [a, b, c] = "abc"; // ["a", "b", "c"] let [one, two, three] = new Set([1, 2, 3]);
할당 연산자 좌측엔 뭐든지 올 수 있습니다.
let user = {}; [user.name, user.surname] = "Bora Lee".split(' '); alert(user.name); // Bora
.entries()로 반복하기
// 객체에 활용 let user = { name: "John", age: 30 }; for (let [key, value] of Object.entries(user)) { alert(`${key}:${value}`); // name:John, age:30이 차례대로 출력 } // map에 활용 let user = new Map(); user.set("name", "John"); user.set("age", "30"); for (let [key, value] of user) { alert(`${key}:${value}`); // name:John, then age:30 }
변수 교환 트릭
let guest = "Jane"; let admin = "Pete"; // 변수 guest엔 Pete, 변수 admin엔 Jane이 저장되도록 값을 교환함 [guest, admin] = [admin, guest]; alert(`${guest} ${admin}`); // Pete Jane(값 교환이 성공적으로 이뤄졌습니다!
...
를 붙인 매개변수 하나를 추가하면 ‘나머지(rest)’ 요소를 가져올 수 있음let [name1, name2, ...rest] = ["Julius", "Caesar", "Consul", "of the Roman Republic"];
alert(name1); // Julius
alert(name2); // Caesar
// `rest`는 배열입니다.
alert(rest[0]); // Consul
alert(rest[1]); // of the Roman Republic
alert(rest.length); // 2
rest
대신에 다른 이름을 사용해도 되는데, 변수 앞의 점 세 개(...
)와 변수가 가장 마지막에 위치해야 함=
을 이용하면 할당할 값이 없을 때 기본으로 할당해 줄 값인 '기본값(default value)'을 설정 가능// 기본값
let [name = "Guest", surname = "Anonymous"] = ["Julius"];
alert(name); // Julius (배열에서 받아온 값)
alert(surname); // Anonymous (기본값)
// name의 prompt만 실행됨
let [surname = prompt('성을 입력하세요.'), name = prompt('이름을 입력하세요.')] = ["김"];
alert(surname); // 김 (배열에서 받아온 값)
alert(name); // prompt에서 받아온 값
let {var1, var2} = {var1:…, var2:…}
let options = {
title: "Menu",
width: 100,
height: 200
};
let {title, width, height} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
// let {...} 안의 순서가 바뀌어도 동일하게 동작함
let {height, width, title} = {
title: "Menu",
height: 200,
width: 100
}
분해하려는 객체의 프로퍼티: 목표 변수
let options = {
title: "Menu",
width: 100,
height: 200
};
// { 객체 프로퍼티: 목표 변수 }
let {width: w, height: h, title} = options;
// width -> w
// height -> h
// title -> title
alert(title); // Menu
alert(w); // 100
alert(h); // 200
let options = {
title: "Menu"
};
let {width = 100, height = 200, title} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
let options = {
title: "Menu"
};
let {width: w = 100, height: h = 200, title} = options;
alert(title); // Menu
alert(w); // 100
alert(h); // 200
let options = {
title: "Menu",
width: 100,
height: 200
};
// title만 변수로 뽑아내기
let { title } = options;
alert(title); // Menu
let options = {
title: "Menu",
height: 200,
width: 100
};
// title = 이름이 title인 프로퍼티
// rest = 나머지 프로퍼티들
let {title, ...rest} = options;
// title엔 "Menu", rest엔 {height: 200, width: 100}이 할당됩니다.
alert(rest.height); // 200
alert(rest.width); // 100
let
없이 사용하기let title, width, height; // 에러가 발생하지 않습니다. ({title, width, height} = {title: "Menu", width: 200, height: 100}); alert( title ); // Menu
let options = {
size: {
width: 100,
height: 200
},
items: ["Cake", "Donut"],
extra: true
};
// 코드를 여러 줄에 걸쳐 작성해 의도하는 바를 명확히 드러냄
let {
size: { // size는 여기,
width,
height
},
items: [item1, item2], // items는 여기에 할당함
title = "Menu" // 분해하려는 객체에 title 프로퍼티가 없으므로 기본값을 사용함
} = options;
alert(title); // Menu
alert(width); // 100
alert(height); // 200
alert(item1); // Cake
alert(item2); // Donut
function({
incomingProperty: varName = defaultValue
...
})
let options = {
title: "My menu",
items: ["Item1", "Item2"]
};
function showMenu({
title = "Untitled",
width: w = 100, // width는 w에,
height: h = 200, // height는 h에,
items: [item1, item2] // items의 첫 번째 요소는 item1에, 두 번째 요소는 item2에 할당함
}) {
alert( `${title} ${w} ${h}` ); // My Menu 100 200
alert( item1 ); // Item1
alert( item2 ); // Item2
}
showMenu(options);
showMenu({}); // 모든 인수에 기본값이 할당됩니다.
showMenu(); // 에러가 발생할 수 있습니다.
function showMenu({ title = "Menu", width = 100, height = 200 } = {}) {
alert( `${title} ${width} ${height}` );
}
showMenu(); // Menu 100 200
요약
- 자바스크립트에선 Date 객체를 사용해 날짜와 시간을 나타냅니다. Date 객체엔 ‘날짜만’ 혹은 ‘시간만’ 저장하는 것은 불가능하고, 항상 날짜와 시간이 함께 저장됩니다.
- 월은 0부터 시작합니다(0은 1월을 나타냅니다).
- 요일은 getDay()를 사용하면 얻을 수 있는데, 요일 역시 0부터 시작합니다(0은 일요일을 나타냅니다).
- 범위를 넘어가는 구성요소를 설정하려 할 때 Date 자동 고침이 활성화됩니다. 이를 이용하면 월/일/시간을 쉽게 날짜에 추가하거나 뺄 수 있습니다.
- 날짜끼리 빼는 것도 가능한데, 이때 두 날짜의 밀리초 차이가 반환됩니다. 이게 가능한 이유는 Date 가 숫자형으로 바뀔 때 타임스탬프가 반환되기 때문입니다.
- Date.now()를 사용하면 현재 시각의 타임스탬프를 빠르게 구할 수 있습니다.
- 자바스크립트의 타임스탬프는 초가 아닌 밀리초 기준이라는 점을 항상 유의하시기 바랍니다.
- 간혹 밀리초보다 더 정확한 시간 측정이 필요할 때가 있습니다. 자바스크립트는 마이크로초(1/1,000,000초)를 지원하진 않지만 대다수의 호스트 환경은 마이크로초를 지원합니다. 브라우저 환경의 메서드 performance.now()는 페이지 로딩에 걸리는 밀리초를 반환해주는데, 반환되는 숫자는 소수점 아래 세 자리까지 지원합니다.
Date
객체를 활용하면 생성 및 수정 시간을 저장하거나 시간을 측정할 수 있고, 현재 날짜를 출력하는 용도 등으로도 활용할 수 있음Date 객체 생성:new Date()
new Date()
: 인수 없이 호출하면 현재 날짜와 시간이 저장된 Date 객체가 반환됩니다.
new Date(milliseconds)
: UTC 기준(UTC+0) 1970년 1월 1일 0시 0분 0초에서 milliseconds 밀리초(1/1000 초) 후의 시점이 저장된 Date 객체가 반환됨
let now = new Date();
alert( now ); // 현재 날짜 및 시간이 출력됨
// 1970년 1월 1일 0시 0분 0초(UTC+0)를 나타내는 객체
let Jan01_1970 = new Date(0);
alert( Jan01_1970 );
// 1970년 1월 1일의 24시간 후는 1970년 1월 2일(UTC+0)임
let Jan02_1970 = new Date(24 * 3600 * 1000);
alert( Jan02_1970 );
타임스탬프(timestamp): 1970년의 첫날을 기준으로 흘러간 밀리초를 나타내는 정수
1970년 1월 1일 이전 날짜에 해당하는 타임스탬프 값은 음수
new Date(datestring)
인수가 하나인데, 문자열이라면 해당 문자열은 자동으로 구문 분석(parsed)됨
new Date(year, month, date, hours, minutes, seconds, ms)
주어진 인수를 조합해 만들 수 있는 날짜가 저장된 객체가 반환됩니다(지역 시간대 기준, 첫 번째와 두 번째 인수만 필수값)
- year
는 반드시 네 자리 숫자여야 합니다. 2013은 괜찮고 98은 괜찮지 않습니다.
- month
는 🚨0(1월)부터 11(12월) 사이의 숫자 여야 합니다.
- date
는 일을 나타내는데, 값이 없는 경우엔 1일로 처리됩니다.
- hours/minutes/seconds/ms
에 값이 없는 경우엔 0으로 처리됩니다.
최소 정밀도는 1밀리초(1/1000초)
getFullYear()
연도(네 자릿수)를 반환합니다.
getMonth()
월을 반환합니다(0 이상 11 이하)
getDate()
일을 반환합니다(1 이상 31 이하)
getHours()
, getMinutes()
, getSeconds()
, getMilliseconds()
시, 분, 초, 밀리초를 반환합니다.
getDay()
일요일을 나타내는 0부터 토요일을 나타내는 6까지의 숫자 중 하나를 반환합니다.
위의 메서드 모두는 현지 시간 기준 날짜 구성요소를 반환합니다.
위 메서드 이름에 있는 ‘get’ 다음에 'UTC’를 붙여주면 표준시(UTC+0) 기준의 날짜 구성 요소를 반환해주는 메서드 getUTCFullYear()
, getUTCMonth()
, getUTCDay()
를 만들 수 있습니다.
getYear()
가 아닌getFullYear()
를 사용하세요.
getYear()
는 두 자릿수 연도를 반환하는 경우가 있기 때문에 절대 사용해선 안 됩니다.
getTime()
getTimezoneOffset()
setFullYear(year, [month], [date])
setMonth(month, [date])
setDate(date)
setHours(hour, [min], [sec], [ms])
setMinutes(min, [sec], [ms])
setSeconds(sec, [ms])
setMilliseconds(ms)
setTime(milliseconds)
(1970년 1월 1일 00:00:00 UTC부터 밀리초 이후를 나타내는 날짜를 설정)let today = new Date();
today.setHours(0);
alert(today); // 날짜는 변경되지 않고 시만 0으로 변경됩니다.
today.setHours(0, 0, 0, 0);
alert(today); // 날짜는 변경되지 않고 시, 분, 초가 모두 변경됩니다(00시 00분 00초).
let date = new Date(2013, 0, 32); // 2013년 1월 32일은 없습니다.
alert(date); // 2013년 2월 1일이 출력됩니다.
let date = new Date();
date.setSeconds(date.getSeconds() + 70);
alert( date ); // 70초 후의 날짜가 출력됩니다.
let date = new Date(2016, 0, 1); // 2016년 1월 1일
date.setDate(0); // 일의 최솟값은 1이므로 0을 입력하면 전 달의 마지막 날을 설정한 것과 같은 효과를 봅니다.
alert( date ); // 31 Dec 2015
Date
객체를 숫자형으로 변경하면 타임스탬프(date.getTime()
을 호출 시 반환되는 값)가 됨let date = new Date();
alert(+date); // 타임스탬프(date.getTime()를 호출한 것과 동일함)
let start = new Date(); // 측정 시작
// 원하는 작업을 수행
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = new Date(); // 측정 종료
alert( `반복문을 모두 도는데 ${end - start} 밀리초가 걸렸습니다.` );
Date.now()
는 new Date().getTime()
과 의미론적으로 동일하지만 중간에 Date
객체를 만들지 않는다는 점이 다릅니다. 따라서 new Date().getTime()
를 사용하는 것보다 빠르고 가비지 컬렉터의 일을 덜어준다는 장점이 있습니다.let start = Date.now(); // 1970년 1월 1일부터 현재까지의 밀리초
// 원하는 작업을 수행
for (let i = 0; i < 100000; i++) {
let doSomething = i * i * i;
}
let end = Date.now(); // done
alert( `반복문을 모두 도는데 ${end - start} 밀리초가 걸렸습니다.` ); // Date 객체가 아닌 숫자끼리 차감함
Date.parse(str)
를 사용하면 문자열에서 날짜를 읽어올 수 있습니다.YYYY-MM-DDTHH:mm:ss.sssZ
처럼 생겨야 합니다.YYYY-MM-DD
– 날짜(연-월-일)"T"
– 구분 기호로 쓰임HH:mm:ss.sss
– 시:분:초.밀리초'Z'
(옵션) – +-hh:mm
형식의 시간대를 나타냄. Z
한 글자인 경우엔 UTC+0
을 나타냄YYYY-MM-DD
, YYYY-MM
, YYYY
같이 더 짧은 문자열 형식도 가능합니다.Date.parse(str)
를 호출하면 문자열과 대응하는 날짜의 타임스탬프가 반환됩니다. 문자열의 형식이 조건에 맞지 않은 경우엔 NaN이 반환됩니다.요약
- JSON은 독자적인 표준을 가진 데이터 형식으로, 대부분의 언어엔 JSON을 쉽게 다룰 수 있게 해주는 라이브러리가 있습니다.
- JSON은 일반 객체, 배열, 문자열, 숫자, 불린값, null을 지원합니다.
- JSON.stringify를 사용하면 원하는 값을 JSON으로 직렬화 할 수 있고, JSON.parse를 사용하면 JSON을 본래 값으로 역 직렬화 할 수 있습니다.
- 위 두 메서드에 함수를 인수로 넘겨주면 원하는 값만 읽거나 쓰는 게 가능합니다.
- JSON.stringify는 객체에 toJSON 메서드가 있으면 이를 자동으로 호출해줍니다.
JSON.stringify
– 객체를 JSON으로 바꿔줍니다.JSON.parse
– JSON을 객체로 바꿔줍니다.let json = JSON.stringify(value[, replacer, space])
value
: 인코딩 하려는 값replacer
: JSON으로 인코딩 하길 원하는 프로퍼티가 담긴 배열. 또는 매핑 함수 function(key, value)space
: 서식 변경 목적으로 사용할 공백 문자 수let room = {
number: 23
};
let meetup = {
title: "Conference",
participants: [{name: "John"}, {name: "Alice"}],
place: room // meetup은 room을 참조합니다
};
room.occupiedBy = meetup; // room은 meetup을 참조합니다
alert( JSON.stringify(meetup, function replacer(key, value) {
alert(`${key}: ${value}`);
return (key == 'occupiedBy') ? undefined : value;
}));
/* replacer 함수에서 처리하는 키:값 쌍 목록
: [object Object]
title: Conference
participants: [object Object],[object Object]
0: [object Object]
name: John
1: [object Object]
name: Alice
place: [object Object]
number: 23
*/
JSON.stringify(value, replacer, space)
의 세 번째 인수 space
는 가독성을 높이기 위해 중간에 삽입해 줄 공백 문자 수를 나타냅니다.toString
을 사용해 객체를 문자형으로 변환시키는 것처럼, 객체에 toJSON
이라는 메서드가 구현되어 있으면 객체를 JSON
으로 바꿀 수 있을 겁니다. JSON.stringify
는 이런 경우를 감지하고 toJSON을 자동으로 호출해줍니다.JSON.parse를 사용하면 JSON으로 인코딩된 객체를 다시 객체로 디코딩 할 수 있습니다.
let value = JSON.parse(str, [reviver]);
str
: JSON 형식의 문자열reviver
: 모든 (key, value) 쌍을 대상으로 호출되는 function(key,value) 형태의 함수로 값을 변경시킬 수 있습니다.let str = '{"title":"Conference","date":"2017-11-30T12:00:00.000Z"}';
let meetup = JSON.parse(str, function(key, value) {
if (key == 'date') return new Date(value);
return value;
});
alert( meetup.date.getDate() ); // 이제 제대로 동작하네요!