JavaScript Builtins (내장 객체 및 생성자)
JSON
프로그래밍을 하다 보면 ‘프로그래밍 언어에서 사용하는 자료구조’를 보조기억장치에 저장하거나,
혹은 네트워크를 통해 전송해야 할 일이 생깁니다.
이 때 자료구조를 그대로 저장/전송할 수는 없으니, 저장/전송 가능한 형태로 변환하는 절차가 필요합니다.
이 절차를 일러 직렬화(serialization)라고 합니다.
반대로, 직렬화된 데이터를 프로그래밍 언어에서 다시 사용할 수 있도록 변환해주는 절차를 역직렬화(deserialization)라고 합니다.
JSON(JavaScript Object Notation)은 웹의 세계에서는 가장 많이 사용되는 직렬화 형식입니다.
그 이름이 말해주듯이, JavaScript 객체와 유사한 표기법을 사용하는 텍스트를 통해 복잡한 자료구조를 나타냅니다.
// 자바스크립트 같아보여도 자바스크립트가 아닙니다. (단지 표기법이 닮았을 뿐!)
{
name: "siwon",
phone: "010-8882-4335",
email: "ksiinw@naver.com",
company: "22div",
birthday: "1996/02/05"
},
{
"key" : "value",
"arr" : [1,2,3],
"nullProp" : null
}
JSON 은 언어에 관계없이 사용할 수 있는 직렬화 형식이고,
실제로 많은 프로그래밍 언어들이 JSON 관련 기능을 내장하고 있습니다.
JavaScript 역시 JSON 관련 기능을 내장하고 있습니다.
같은 이름의 JSON 내장 객체의 메소드를 통해 직렬화와 역직렬화를 할 수 있습니다.
직렬화와 역직렬화는 무엇일까?
직렬화 - 객체를 직렬화하여 전송 가능한 형태로 만들어 주는 것을 말하며,
객체들의 데이터를 연속적인 데이터로 변형하여 Stream 을 통해 데이터를 읽도록 해줍니다.
주로 객체들을 통채로 파일로 저장하거나 전송하고 싶을 때 주로 사용됩니다.
역직렬화 - 직렬화된 파일 등을 역으로 직렬화하여 다시 객체의 형태로 만드는 것을 의미합니다.
저장된 파일을 읽거나 전송된 스트림 데이터를 읽어 원래 객체의 형태로 복원합니다.
// `JSON.stringify`로 직렬화를 할 수 있습니다.
JSON.stringify({
key: "value",
arr: [1, 2, 3],
nullProp: null,
undefinedProp: undefined // 값이 `undefined`인 속성은 직렬화 과정에서 제외됩니다.
}); // '{"key":"value","arr":[1,2,3],"nullProp":null}'
// `JSON.parse`로 역직렬화를 할 수 있습니다.
JSON.parse('{"key":"value","arr":[1,2,3],"nullProp":null}');
JSON.stringify
, JSON.parse
의 사용법 및 특징
// 직렬화
let book = {
title: "bookname",
authors: ["Kendrick"],
edition: 3,
year: 2011
};
let jsonText = JSON.stringify(book);
console.log(jsonText);
// {"title":"bookname","authors":["Kendrick"],"edition":3,"year":2011} 출력
//특정 객체만 받음
let jsonText = JSON.stringify(book, ["title", "edition"]);
console.log(jsonText);
// {"title":"bookname","edition":3} 출력
// stringify의 몇가지 옵션
// 1. 콜백함수로 컨트롤
let jsonText = JSON.stringify(book, function(key, value) {
switch (key) {
case "authors":
return value.json(",");
case "year":
return 2000;
default:
return value;
}
});
// 2. 문자열 들여쓰기, 들여쓰기가 지정되어 저장됩니다.(자동으로 줄바꿈도 합니다.)
let jsonText = JSON.stringify(book, null, 4);
// 3. toJSON() 메서드사용
let book = {
title: "bookname",
authors: ["Kendrick"],
edition: 3,
year: 2011,
toJSON: function() {
return this.title;
}
};
// stringify()시 순서는 아래와 같습니다.
// 1. toJSON()값이 있으면 호출하여 사용
// 2. 두번째 매개변수 있다면 필터 적용
// 3. 새번째 매개변수가 있다면 그에 따라 형식 조절
// parse의 옵션
let book = {
title: "bookname",
authors: ["Kendrick"],
edition: 3,
year: 2011,
releaseDate: new Date(2011, 11, 1)
};
let jsonText = JSON.stringify(book);
// JSON.parse()시 알아서 json스트링이 객체로 파싱됩니다.
let bookCopy = JSON.parse(jsonText, function(key, value) {
if (key == "releaseDate") {
return new Date(value);
} else {
return value;
}
});
JSON 을 직접 편집해야 할 때는 JSON 이 JavaScript 가 아니라는 사실에 주의해야 합니다.
- 속성 이름은 꼭 쌍따옴표를 둘러주어야 합니다.
- Map, Set, Date, Error, RegExp, Function, Promise 와 같이 특별한 동작방식을 가지는 객체들을 제대로 표현할 수 없습니다.
- undefined, NaN, Infinity 과 같은 값을 표현할 수 없습니다.
- 주석을 쓸 수 없습니다.
Date 객체
JavaScript 에는 날짜과 시각을 다루기 위한 Date 생성자가 내장되어 있습니다.
이를 사용하기 위해서 일단 아래의 개념들을 알아둘 필요가 있습니다.
협정 세계시 (UTC) - 지구 상의 여러 지역에서는 시간대(timezone)와 일광 절약 시간제(DST)에 따라 서로 다른 시각을 사용합니다.
이 때문에 발생하는 혼동을 피하기 위해 위치 및 DST 의 사용 여부와 상관 없이 같은 기준으로 시각을 다룰 필요가 있는데,
이 때 사용되는 것이 협정 세계시(UTC)입니다.
UTC 가 만들어지기 이전에는 그리니치 평균시(GMT)라는 용어가 널리 쓰였습니다.
또한 조금씩 느려지는 지구 자전 속도에 맞추기 위해 UTC 에는 가끔씩 윤초(leap second)가 추가되기도 합니다.
유닉스 시간 - 컴퓨터에서는 시간 데이터를 편하게 다루기 위해서 유닉스 시간이라는 특별한 단위를 사용합니다.
유닉스 시간은 UTC 기준 1970 년 1 월 1 일 0 시 0 분 0 초부터 경과한 시간을 초 단위의 정수로 나타냅니다.
예를 들어, 한국 시간대의 2017-12-10 12:26:11 라는 시간을 유닉스 시간으로 나타내면 1512876371 이 됩니다.
유닉스 시간은 POSIX 시간 또는 Epoch 시간이라는 이름으로 불리기도 합니다.
Date 객체의 생성
Date 생성자는 아래와 같은 방법으로 사용할 수 있습니다.
- new Date() - 현재 시각을 나타내는 Date 객체를 반환합니다.
- new Date(value) - value 가 정수인 경우, 이를 밀리초 단위의 유닉스 시간으로 간주해서 이에 해당하는 Date 객체를 반환합니다.
value 가 문자열인 경우, 이 문자열이 나타내는 Date 객체를 반환합니다. - new Date(year, month, day, hour, minutes, seconds, milliseconds) - 년, 월, 일, 시, 분, 초, 밀리초를
직접 입력해서 Date 객체를 생성할 수도 있습니다.
‘월’ 부분은 0 부터 11 까지의 값을 가집니다. 월 이후의 인수는 생략가능하고, 인수를 생략하면 ‘일’은 1 로, 나머지는 모두 0 으로 지정됩니다.
Date 객체를 생성하고 난 뒤에는, 해당 객체가 나타내는 년, 월, 일, 시, 분, 초, 밀리초를 가져오거나 변경할 수 있습니다.
문자열로 변환하기
Date 객체가 나타내는 시각을 여러 가지 방법으로 문자열로 변환할 수도 있습니다.
const now = new Date();
console.log(now.toString()); // Sun Dec 10 2017 12:49:31 GMT+0900 (KST)
console.log(now.toLocaleString()); // 2017. 12. 10. 오후 12:49:31
console.log(now.toDateString()); // Sun Dec 10 2017
console.log(now.toTimeString()); // 12:49:31 GMT+0900 (KST)
console.log(now.toISOString()); // 2017-12-10T03:49:31.145Z (국제표준표기법으로 문자열로 바꿔줍니다.)
console.log(now.toUTCString()); // Sun, 10 Dec 2017 03:49:31 GMT
시간 간격 측정하기
- 연산자를 사용해서 두 Date 객체 사이의 시간 간격이 얼마나 되는지를 밀리초 단위로 측정할 수 있습니다.
const start = new Date();
alert("시간이 가고 있습니다...");
const end = new Date();
alert(`${end - start} 밀리초 만큼의 시간이 경과되었습니다.`);
라이브러리 사용하기
JavaScript 에 내장되어 있는 Date 객체는 컴퓨터에서 시간 데이터를 다루기 위한 기본적인 기능들을 제공하지만,
실제 서비스에서 사용하기에는 부족한 점이 많습니다.
이 때문에, 실무에서는 moment.js 혹은 date-fns 와 같은 별도의 라이브러리를 사용하는 경우가 많습니다.
(실무에서는 왠만하면 date 보다는 moment.js 를 씁다고 합니다.)
const moment = require("moment");
moment.locale("ko");
const now = moment();
console.log(now.format("dddd, MMMM Do YYYY, h:mm:ss a")); // 일요일, 12월 10일 2017, 1:02:42 오후
console.log(now.subtract(7, "days").calendar()); // 2017.12.03
console.log(moment("20120101", "YYYYMMDD").fromNow()); // 6년 전
Date 객체를 이용하여 알림 간단히 구현하기
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
h3 {color : pink;
border : pink solid 2px;
width : 285px;}
</style>
<title>알람 어플리케이션</title>
</head>
<body onload = "CurrentTime()" />
현재 시간 : <input type="text", id="curTime" /><br /><br />
알람 시간 : <input type="text", id="alhour" style="width:40px;" /> 시
<input type="text", id="almin" style="width:40px;" /> 분
<h3>시간을 입력해 주세요 (예 : 14시 20분)</h3>
</body>
</html>
// js
function CurrentTime() {
let time = new Date();
cHour = time.getHours();
cMin = time.getMinutes();
cSec = time.getSeconds();
document.querySelector("#curTime").value = cHour + ":" + cMin + ":" + cSec;
let nalhour = Number(document.querySelector("#alhour").value);
let nalmin = Number(document.querySelector("#almin").value);
if (cHour == nalhour && cMin == nalmin) alert("이제 시간 됬어요!!");
else setTimeout("CurrentTime()", 1000);
}
console.log(new Date());
console.log(setTimeout("CurrentTime()", 1000));
Symbol
심볼은 ES2015 에서 도입된 새로운 원시 타입입니다.
Symbol 내장 함수를 통해 새 심볼을 생성할 수 있습니다.
const sym = Symbol();
console.log(typeof sym); // symbol
console.log(sym); // Symbol()
Symbol 함수에 문자열을 넘겨서, 해당 심볼에 대한 설명을 포함한 심볼을 생성할 수 있습니다.
이 때 넘겨지는 문자열은 그저 심볼의 설명일 뿐이므로, 심볼의 비교 연산에 영향을 주지 않습니다.
즉, 어떤 문자열이 인수열에 들어오는 지와 상관없이 새로 생성된 심볼은 다른 모든 심볼과 다른 것으로 취급됩니다.
console.log(Symbol("my symbol")); // Symbol(my symbol)
console.log(Symbol("my symbol") === Symbol("my symbol")); // false
console.log(Symbol() === Symbol()); // false
심볼은 객체의 속성 키로 사용될 수 있습니다.
const mySymbol = Symbol("my symbol");
const obj = {
[mySymbol]: "hello"
};
console.log(obj); // { [Symbol(my symbol)]: 'hello' }
내장 심볼(well-known symbol)을 객체의 속성 키로 사용하면, 특정 상황에서의 객체의 동작 방식을 마음대로 바꿀 수 있습니다.
Symbol.hasInstance
- 객체가 instanceof 연산자의 피연산자로 왔을 때의 동작을 바꿉니다.Symbol.isConcatSpreadable
- 객체가 Array.prototype.concat 메소드의 인수로 넘겨질 때의 동작을 바꿉니다.Symbol.iterator
- 객체가 for…of 구문을 통해 사용될 때의 동작 방식을 바꿉니다.Symbol.match
- 객체가 String.prototype.match 메소드의 인수로 넘겨질 때의 동작을 바꿉니다.Symbol.replace
- 객체가 String.prototype.replace 메소드의 인수로 넘겨질 때의 동작을 바꿉니다.Symbol.search
- 객체가 String.prototype.search 메소드의 인수로 넘겨질 때의 동작을 바꿉니다.Symbol.species
- Array.prototype 을 상속받은 객체에 대해 Array.prototype.map 등의 메소드를 호출할 때, 반환되는 객체의 생성자를 지정합니다.Symbol.split
- 객체가 String.prototype.split 메소드의 인수로 넘겨질 때의 동작을 바꿉니다.Symbol.toPrimitive
- 객체가 원시 타입의 값으로 변환되어야 할 때, 정확이 어떤 값으로 변환되어야 하는 지를 지정합니다.Symbol.toStringTag
- Object.prototype.toString() 메소드를 객체에 대해 직접 호출할 때의 동작을 바꿉니다.Symbol.unscopable
- with 블록 안에서 어떤 속성을 참조할 수 있는 지를 지정합니다.
ES5 에서 @@이 ES6 에서 Symbol 로 더 유연하게 코드를 작성할 수 있도록 해줬습니다.
라이브러리를 만들거나 프레임워크를 만드는 일을 하지 않는 이상, 자주 사용할 일은 없을 것 같습니다.
그래도 혹시 모르니 알고 가도록 합니다.
Map
ES2015 에서 도입된 Map 생성자는 객체와 유사하게 키-값 쌍(key-value pair)을 저장할 수 있는 새로운 자료구조를 제공합니다.
(배열의 map 메소드와 전혀 다른 것이니 헷갈리면 안됩니다.)
const m = new Map();
m.set("hello", "world");
console.log(m.get("hello")); // 'world'
console.log(m.has("hello")); // true
m.delete("hello");
console.log(m.get("hello")); // undefined
console.log(m.has("hello")); // false
Map 으로 생성된 객체는, 일반적인 객체와 비교했을 때 아래와 같은 차이점을 갖습니다.
객체는 속성 접근자(property accessor) 문법을 통해서, Map 은 메소드를 통해서 내부의 데이터를 조작합니다.
문자열과 심볼만이 객체의 속성 키가 될 수 있는 반면, 어떤 값이라도 Map 객체의 키로 사용될 수 있습니다.
객체의 속성을 확인할 때는 프로토타입 체인을 확인하는 과정에 필요하지만, Map 객체 안에 들어있는 데이터는 프로토타입의 영향을 받지 않습니다.
Map 객체의 size 속성을 통해 내부에 들어있는 데이터의 개수를 쉽게 알 수 있습니다.
Map 객체는 데이터의 추가/삭제가 빈번하게 일어나는 경우 일반적인 객체보다 훨씬 빠르게 동작한다는 장점이 있는 반면,
JSON 등으로 직렬화 하기 어렵다는 특징이 있습니다.
키-값 쌍 형태의 데이터를 다루면서 속도가 중요한 경우에는 Map 을 사용하는 것이 좋습니다.
Set
ES2015 에서 도입된 Set 생성자는 집합 형태의 자료구조를 제공합니다.
Set 객체 내부에 이미 존재하는 데이터를 추가하려고 하면, 이는 무시됩니다.
즉, Set 객체는 내부에 중복된 데이터가 저장되는 것을 허용하지 않습니다.
const s = new Set();
s.add(1); // add라는 메소드를 이용하여 내용을 추가
s.add(1);
s.add(2);
console.log(s); // Set { 1, 2 } - 중복되는 데이터는 무시하고 추가되었음을 알 수 있습니다.
배열과 유사한 형태의 자료구조가 필요하지만 순서가 중요하지 않은 경우,
그리고 중복된 데이터의 저장을 허용하지 않아야 할 경우, Set 을 쓰면 좋습니다.
// 중복 제거된 값을 새로운 배열로 받는 방법
const s = new Set([1, 2, 3, 1, 2, 3, 4]);
const arr = Array.from(s);
// 글자중에서 중복된 글자를 제거하는 방법 ex) tomato -> toma
function removeDuplicates(str) {
return Array.from(new Set(str)).join("");
}
기타
이 밖에, ES2015 또는 그 이후에 추가된 여러 가지 내장 객체 및 생성자들과 그 설명을 아래에 나열합니다.
Proxy
- 다른 객체처럼 행세하면서, 특정한 행동에 대해서는 다른 동작방식을 보이는 새로운 객체를 만들고 싶을 때 사용합니다.Reflect
- Reflect 객체의 메소드를 통해, JavaScript 의 몇 가지 내장 기능을 메소드로서 사용할 수 있습니다.Intl
- Intl 객체를 이용하면, 사용 중인 언어에 따라 문자열 비교, 숫자 표현 형식, 시간 표현 형식을 바꿀 수 있습니다.WeakMap
- Map 생성자와 사용법이 같지만, 키로 사용된 값에 대한 메모리 누수를 방지할 수 있습니다.WeakSet
- Set 생성자와 사용법이 같지만, 집합에 추가된 값에 대한 메모리 누수를 방지할 수 있습니다.TypedArray
- 이진 데이터(binary data)를 다룰 수 있는 방법을 제공합니다. File API, Canvas, Fetch API 등에서 사용됩니다.
2. Today I Found Out
내장 객체 및 생성자에 대해서 공부를 하면서
JSON, DATE객체, Symbol, Map, Set을 배울수 있었습니다.
JSON 파일을 공부하면서 과연 객체형식의 파일을 어떻게 서버와 주고 통신을 할 수 있을지
브레이킹이후에 수업이 너무 기대되었고, Date객체를 이용하여 간단한 알람기능을 구현해보면서
Date객체랑 조금 친해질 수 있었습니다.
그밖에 새롭게 ES2015에서 추가된 Symbol, Map, Set등을 공부하면서
실무에서 사용하는 기능도 있지만 라이브러리와 API개발등에 도움이 되는
기능들을 알아보았고, 앞으로 제가 직접 그런 기술들을 다룰지는 모르겠지만
새로이 추가되는 기능들이 앞으로의 JS를 활용한 개발에 있어
많은 도움이 되어줄 거라는 생각이 들었습니다.
3. refer
https://jaeyeophan.github.io/2017/04/20/ES6-8-Symbol/
https://helloworldjavascript.net/pages/250-builtins.html