[리액트 스타터2] 2장. 자바스크립트 실전
1. 구조 분해 할당
- 배열이나 객체에서 요소를 해체해 개별 변수에 그 값을 담을 때 사용한다.
< 배열의 구조 분해 할당 >
let arr = [1, 2, 3];
let one = arr[0];
let two = arr[1];
let three = arr[2];
console.log(one, two, three); // 1 2 3
let arr = [1, 2, 3];
let [one, two, three] = arr; ①
console.log(one, two, three); // 1 2 3
위는 배열의 구조 분해 할당한 예이다.
let arr = [1, 2, 3];
let [one, two] = arr;
console.log(one, two); // 1 2
- 배열의 길이와 할당할 변수의 개수가 일치하지 않아도 오류가 발생하지 않는다.
let arr = [1, 2];
let [one, two, three] = arr;
console.log(one, two, three); // 1 2 undefiend
- 할당할 변수의 개수가 배열의 길이보다 많아도 오류가 발생하지 않는다.
- 다만 배열의 길이를 넘는 변수에는 undefined가 할당된다.
< 객체의 구조 분해 할당 >
let person = {
name: "이정환",
age: 25,
location: "경기도"
};
let { name, age, location } = person;
console.log(name, age, location);
// 이정환 25 경기도
- 데이터 저장 순서가 아니라 key를 기준으로 할당된다.
< 함수의 매개변수가 객체일 때 구조 분해 할당하기 >
function func({ name, age, location }) {
console.log(name, age, location);
}
let person = {
name: "이정환",
age: 25,
location: "경기도"
};
func(person);
// 이정환 25 경기도
function func({ name: n, age: a, location: l }) {
console.log(n, a, l);
}
let person = {
name: "이정환",
age: 25,
location: "경기도"
};
func(person);
// 이정환 25 경기도
- 객체의 구조 분해 할당 과정에서 매개변수의 이름을 새롭게 바꿀 수 있다.
- 변수 이름 옆에 콜론:과 함께 새 변수명을 쓰면, 새 이름으로 값이 할당된다.
2. 스프레드 연산자와 rest 매개변수
- 스프레드 연산자: 개별 요소로 분리한다.
- rest 매개변수: 개별 요소를 다시 배열로 묶는다.
< 스프레드 연산자 >
- 스프레드 연산자(전개 연산자): 기호 ‘...’로 표기한다.
- 반복이 가능한 객체의 값을 개별 요소로 분리한다.
let arrA = [1, 2, 3];
let arrB = [...arrA, 4, 5, 6];
console.log(arrB); // [1, 2, 3, 4, 5, 6]
- 만약 arrB = [arrA, 4, 5, 6]이라면 arrB에는 [[1, 2, 3], 4, 5, 6]이 저장된다.
다음은 객체를 다룬 예제이다.
let objA = {
a: 1,
b: 2
};
let objB = {
...objA,
c: 3,
d: 4
};
console.log(objB); // {a: 1, b: 2, c: 3, d: 4}
< 스프레드 연산자와 함수 >
function func(a, b, c) {
console.log(a, b, c);
}
let arr = [1, 2, 3];
func(...arr); ① // 1 2 3
< 매개변수에서 구조 분해 할당과 스프레드 연산자의 차이 >
- 구조 분해 할당: 함수를 호출할 때 전달하는 인수가 1개이고 그 값이 객체인 경우이다.
- 스프레드 연산자: 인수가 1개가 아닌 여러 개로 나뉘어 전달된다. 따라서 매개변수 역시 여러 개 선언한다.
< rest 매개변수 >
- 나머지 매개변수라고 한다.
- 기호 ‘...’으로 표기한다.
- 매개변수로 사용할 변수의 이름 앞에 ... 을 붙이면 rest 매개변수가 된다.
- 스프레드 연산자와 반대로 개별 요소를 배열로 묶는다.
function func(param, ...rest) {
console.log(param);
console.log(rest);
}
func(1, 2, 3, 4);
// 1
// [2, 3, 4]
- 매개변수 param에는 첫 번째 인수 1이, 나머지 인수들은 순차적으로 변수 rest에 배열로 할당된다.
function func(...rest, param) { // 오류 : rest 매개변수는 마지막에 작성해야 합니다.
console.log(param);
console.log(rest);
}
func(1, 2, 3, 4);
- rest 매개변수는 먼저 선언한 매개변수에 할당된 인수를 제외하고 나머지를 모두 배열에 저장한다. 따라서 반드시 매개변수에서 마지막에 선언되어야 한다.
3. 배열과 메서드
{ 요소의 추가 및 삭제 메서드 }
< push >
- 배열 맨 끝에 요소를 추가하고 새로운 길이를 반환한다.
- 무조건 배열 맨 끝에 요소를 추가한다.
- 여러 요소를 추가하려면 콤마로 구분해 전달하면 된다.
let food = ["짜장면", "피자", "치킨"];
const newLength = food.push("탕수육", "라자냐");
console.log(food); // ["짜장면", "피자", "치킨", "탕수육", "라자냐"]
console.log(`새로운 배열의 길이: ${newLength}`); // 새로운 배열의 길이: 5
< pop >
- 배열의 맨 끝 요소를 제거하고, 제거한 요소를 반환한다.
let food = ["짜장면", "피자", "치킨"];
const removedItem = food.pop(); // ①
console.log(removedItem); // 치킨
console.log(food); // ["짜장면", "피자"]
let food = [];
const removedItem = food.pop();
console.log(removedItem); // undefined
console.log(food); // []
- 빈 배열에서 pop 메서드를 사용하면, 제거할 요소가 없기 때문에 undefined를 반환한다.
< shift >
- pop 메서드와 반대로 배열의 맨 앞 요소를 제거하고, 제거한 요소를 반환한다.
let food = ["짜장면", "피자", "치킨"];
const removedItem = food.shift(); // ①
console.log(removedItem); // "짜장면"
console.log(food); // ["피자", "치킨"]
< unshift >
- push와 반대로 배열 맨 앞에 요소를 추가하고, 새 배열의 길이를 반환한다.
let food = ["짜장면", "피자", "치킨"];
const newLength = food.unshift("갈비찜"); // ①
console.log(food); // ["갈비찜", "짜장면", "피자", "치킨"]
console.log(`새로운 배열의 길이: ${newLength}`); // 새로운 배열의 길이: 4
< shift와 unshift는 push나 pop보다 느리다 >
- unshift 메서드로 배열 맨 앞에 요소를 추가할 경우, 새 요소가 인덱스 0이 되어 나머지 배열 요소의 인덱스는 모두 하나씩 뒤로 밀린다.
- 반면 push나 pop 메서드는 배열의 마지막 요소를 추가 또는 제거하는 것이므로 기존 요소들의 인덱스는 변함이 없다.
< slice >
- 기존 배열에서 특정 범위를 잘라 새로운 배열을 반환한다.
- 원본 배열은 수정되지 않는다는 점에 유의하자.
arr.slice(start, end);
- start는 잘라낼 범위의 시작, end는 잘라낼 범위의 끝이다.
- 주의할 점은 end로 범위의 끝을 지정하면, 그 범위는 end 인덱스 전까지이다.
const arr = [1, 2, 3];
const sliced = arr.slice(2); ①
console.log(sliced); // [3]
- start만 전달하고 end를 전달하지 않으면, start부터 배열 끝까지 잘라낸 새 배열을 반환한다.
const arr = [1, 2, 3, 4, 5];
console.log(arr.slice(-1)); ① // [5]
console.log(arr.slice(-2)); // [4, 5]
console.log(arr.slice(-3)); // [3, 4, 5]
console.log(arr.slice(-4)); // [2, 3, 4, 5]
console.log(arr.slice(-5)); // [1, 2, 3, 4, 5]
- 음숫값을 인덱스로 전달해도 된다.
- 만약 end 없이 start만 음수 인덱스로 전달하면, 배열 맨 끝부터 전달한 음수의 절댓값만큼 잘라낸 새 배열을 반환한다.
- 뒤에서부터 셀 때는 -1이 첫 번째 인덱스 번호이다.
< concat >
- 서로 다른 배열을 이어 붙여 새 배열을 반환한다.
- silce 메서드와 같이 원본 배열을 수정하지 않는다.
let arrA = [1, 2];
let arrB = [3, 4];
let arrC = arrA.concat(arrB); ① //
console.log(arrC); // [1, 2, 3, 4]
console.log(arrA); ② // [1, 2]
인수로 배열이 아닌 객체를 전달해 보자.
let arrA = [1, 2];
let arrB = { a: 1, b: 2 };
let arrC = arrA.concat(arrB);
console.log(arrC); // [1, 2, { a: 1, b: 2 } ]
- 인수로 배열을 전달하면 요소를 모두 이어 붙이지만, 객체는 하나의 요소로 인식해 삽입된다.
{ 순회 메서드 }
< forEach >
- 배열의 모든 요소에 순서대로 접근해 특정 동작을 수행한다.
- 인수로 함수를 요구한다.
- 콜백 함수 : 함수 호출 과정에서 인수로 전달되는 함수이다.
- forEach 메서드는 배열 요소 각각을 순회하면서, 인수로 전달한 콜백 함수가 정의한 대로 요소를 동작시킨다.
function cb(item, index, array) {
// 요소에 무언가를 할 수 있습니다.
}
arr.forEach(cb);
- item: 현재 순회하는 배열 요소
- idx: 현재 순회하는 배열 요소의 인덱스
- arr: 순회 중인 배열
function cb(item, idx) {// ②
console.log(`${idx}번째 요소: ${item}`);
}
const arr = [1, 2, 3];
arr.forEach(cb); // ①
// 0번째 요소: 1
// 1번째 요소: 2
// 2번째 요소: 3
- cb는 총 3번 실행된다.
- 함수 표현식이나 화살표 함수로 간략하게 표현할 수 있다.
- 화살표 함수는 앞선 예제의 함수 cb와 동일한 역할을 한다.
const arr = [1, 2, 3];
arr.forEach((item, idx) => { ①
console.log(`${idx}번째 요소: ${item}`);
});
// 0번째 요소: 1
// 1번째 요소: 2
// 2번째 요소: 3
{ 탐색 메서드 }
< indexOf >
- 배열에서 찾으려는 요소의 인덱스를 반환한다.
arr.indexOf(item, fromIndex);
- item: 찾으려는 요솟값
- fromIndex: 탐색을 시작할 인덱스 번호
- formIndex 생략하면 배열의 0번째 인덱스부터 탐색한다.
- fromIndex의 값을 음수로 지정할 수 있는데, 그러면 탐색 위치는 배열의 맨 뒤에서부터 시작한다.
let arr = [1, 3, 5, 7, 1];
console.log(arr.indexOf(1)); // ① 0
console.log(arr.indexOf(1, -1)); // ② 4
- 찾으려는 요소가 배열에 없다면 -1을 반환한다.
- fromIndex의 값이 배열의 길이보다 크거나 같은 경우에도 -1을 반환한다.
- indexOf는 엄격한 비교 연산자(===)로 요소를 비교하므로 자료형이 다르면 다른 값으로 평가한다.
let arr = [1, 3, 5, 7, 1];
console.log(arr.indexOf("3")); // -1
- 객체는 탐색할 수 없다. 객체 자료형은 값을 비교하는 게 아니라 참조값을 비교하기 때문이다.
let arr = [{ name: "이정환" }, 1, 2, 3];
console.log(arr.indexOf({ name: "이정환" })); // -1
< includes >
- 배열에 특정 요소가 있는지 판별한다.
let arr = [1, 3, 5, 7, 1];
console.log(arr.includes(3)); // true
console.log(arr.includes("생선")); // false
< findIndex >
- indexOf처럼 배열에서 찾으려는 요소의 인덱스 번호를 찾아 반환한다.
arr.findIndex( callback(item, index, array) );
- indexOf와는 달리 인수로 콜백 함수를 전달하는데, 이 함수를 ‘판별 함수’라고 한다.
- 배열에서 이 판별 함수를 만족하는 첫 번째 요소의 인덱스를 반환하며, 그런 요소가 없다면 -1을 반환한다.
- 판별 함수에는 3개의 매개변수(현재요소 item, 현재 인덱스 index, 탐색 대상 배열 array)가 제공된다.
function determine(item, idx, arr) { ②
if (item % 2 === 0) {
return true;
} else {
return false;
}
}
let arr = [1, 3, 5, 6, 8];
let index = arr.findIndex(determine); ①
console.log(index); // 3
- findIndex 메서드가 true를 반환하면 탐색에 성공한 것이므로 탐색을 멈춘다.
- 배열 arr에서 처음으로 짝숫값이 나오는 인덱스 번호 3을 반환한다.
다음은 화살표와 삼항 연산자를 이용하여 간결하게 작성한 경우이다.
let arr = [1, 3, 5, 6, 8];
let index = arr.findIndex((item) =>
item % 2 === 0 ? true : false
);
console.log(index); // 3
- 이번에는 findIndex로 배열에서 프로퍼티 name의 값이 “이정환”인 요소의 인덱스 번호를 찾아보겠다.
- indexOf와 달리, findIndex는 판별 함수를 이용해 배열에서 조건과 일치하는 객체 요소를 찾아낸다.
let arr = [
{ name: "이종원" },
{ name: "이정환" },
{ name: "신다민" },
{ name: "김효빈" }
];
let index = arr.findIndex((item) => item.name === "이정환"); ①
console.log(index); // 1
< find >
- findIndex처럼 판별 함수를 전달하고, 배열에서 이를 만족하는 요소를 찾는다.
- 다만 findIndex와 달리 인덱스가 아닌 요소를 반환한다.
let arr = [
{ name: "이종원" },
{ name: "이정환" },
{ name: "신다민" },
{ name: "김효빈" }
];
let element = arr.find((item) => item.name === "이정환"); ①
console.log(element); // {name: "이정환"}
< filter >
- 배열에서 조건을 만족하는 요소만 모아 새로운 배열로 반환한다.
let arr = [
{ name: "이종원", hobby: "축구" },
{ name: "이정환", hobby: "영화" },
{ name: "신다민", hobby: "축구" },
{ name: "김효빈", hobby: "노래" }
];
let filteredArr = arr.filter(
(item) => item.hobby === "축구"
);
console.log(filteredArr);
// 출력 결과
// (2) [Object, Object]
// 0: Object
// name: "이종원"
// hobby: "축구"
// 1: Object
// name: "신다민"
// hobby: "축구"
{ 변형 메서스 }
< map >
- 배열 각각의 요소에 대한 함수 호출 결과를 모아 새 배열을 만들어 반환한다.
arr.map( callback(item, index, array) );
- map 메서드는 콜백 함수를 인수로 전달한다. 이 콜백 함수에는 현재 요소 item, 인덱스 index, map 메서드를 호출한 배열 array이 매개변수로 제공된다.
let arr = [1, 2, 3, 4];
let newArr = arr.map((item) => item * 3); // ①
console.log(newArr); // [3, 6, 9, 12]
- newArr에는 배열 arr의 모든 요소에 3을 곱한 결과가 저장된다.
let arr = [
{ name: "이종원", hobby: "축구" },
{ name: "이정환", hobby: "영화" },
{ name: "신다민", hobby: "축구" },
{ name: "김효빈", hobby: "노래" }
];
let newArr = arr.map((item) => item.name);
console.log(newArr);
- newArr에는 기존 arr에서 사람 이름만 따로 모은 배열이 저장된다.
< sort >
- 배열의 요소를 정렬한다.
- 정렬된 새로운 배열을 반환하는 게 아니라, 기존 배열 요소를 다시 정렬한다는 점에 주의하자.
arr.sort( compare( a, b ) )
- sort 메서드에서는 하나의 콜백 함수를 인수로 전달한다. 이 함수는 비교 함수로 사용되는데, 필수 사항은 아니다.
let arr = [10, 5, 3];
arr.sort();
console.log(arr); // [10, 3, 5]
- 잘못된 결과가 나온 이유는 sort 메서드가 기본적으로 요소를 문자열로 취급해 사전순으로 정렬하기 때문이다.
-> 비교함수 사용하자
function compare(a, b) {
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
let arr = [10, 5, 3];
arr.sort(compare);
console.log(arr); // [3, 5, 10]
- 비교 함수가 양수를 반환 a와 b 중 b의 위치가 a보다 앞이어야 한다는 것을 의미한다.
- 비교 함수가 음수를 반환 a와 b 중 a의 위치가 b보다 앞이어야 한다는 것을 의미한다.
- 비교 함수가 0을 반환 비교 함수가 0을 반환하면, a와 b는 정렬 순서가 동일하다는 것을 의미한다.
< join >
- 배열 요소를 모두 연결해 하나의 문자열로 반환한다.
let arr = ["안녕", "나는", "이정환"];
console.log(arr.join()); // ① 안녕,나는,이정환
console.log(arr.join("-")); // ② 안녕-나는-이정환
< reduce >
- 배열 요소를 모두 순회하면서 인수로 제공한 함수를 실행하고, 단 하나의 결괏값만 반환한다.
- map 메서드와 유사하지만 하나의 결과만을 반환한다는 차이가 있다.
arr.reduce( ( acc, item, index, array ) => {
(...)
}, initial );
- 호출할 때 2개의 인수 (콜백 함수, 초깃값)을 전달한다.
- 첫 번째 인수로 전달하는 콜백 함수를 특별히 ‘리듀서’라고 부른다. 리듀서 함수는 배열의 모든 요소에 대해 각각 실행되는데, 4개의 매개변수를 제공받는다.
- 리듀서에 제공되는 매개변수의 역할은 다음과 같다.
- acc: 누산기(accumulator)라는 뜻으로 이전 함수의 호출 결과를 저장한다. reduce 메서드의 두 번째 인수 initial이 이 acc의 초깃값이 된다.
- item: 현재의 배열 요소이다.
- index: 현재의 배열 인덱스이다.
- array: reduce 메서드를 호출한 배열이다.
- acc의 초깃값인 인수 initial은 필수 사항은 아니며, 전달하지 않으면 배열의 첫 번째 요소가 acc의 초깃값이 된다.
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((acc, item) => acc + item, 0);
console.log(result); // 15
4. Date 객체와 날짜
< date 객체 생성하기 >
let date = new Date();
console.log(date);
// 현재 날짜 및 시간
생성자: 자바스크립트의 생성자는 객체를 생성하는 함수이다. Date 객체는 리터럴이 아닌 생성자로 만든다. 생성자로 객체를 만들 때 특정 시간 등의 정보를 인수로 전달하면, 객체를 생성함과 동시에 초기화할 수 있다.
< 협정 세계시 >
시간의 시작을 1970년 1월 1일 0시 0분 0초를 기준으로 하며, 이 시작 시각을 UTC+0시라고 표현한다.
let Jan01_1970 = new Date(0);
console.log(Jan01_1970);
// Thu Jan 01 1970 09:00:00 GMT+0900 (한국 표준시)
1970년 1월 1일 9시 0분 0초로 출력되는 것을 볼 수 있는데, 이는 한국 표준시가 UTC보다 9시간이 빠르기 때문이다.
< 타임스탬프 >
특정 시간이 UTC+0시인 1970년 첫날을 기준으로 흘러간 밀리초(ms)의 시간을 의미한다.
24시간에 해당하는 밀리초를 인수로 전달하면, UTC+0에서 하루가 지난 값을 반환한다.
하루에 해당하는 밀리초는 24 * 3600 * 1000이다.
다음과 같이 생성된 Date 객체에서 역으로 타임스탬프를 구할 수도 있다.
let Jan02_1970 = new Date(24 * 3600 * 1000);
console.log(Jan02_1970.getTime());
// 86400000
{ 원하는 날짜로 Date 객체 생성하기 }
< 문자열로 특정 날짜 전달하기 >
다음 4개의 Date 객체 생성자에 인수로 전달하는 날짜는 각각 다른 형식의 문자열이다.
let date1 = new Date("2000-10-10/00:00:00"); ①
let date2 = new Date("2000.10.10/00:00:00"); ②
let date3 = new Date("2000/10/10/00:00:00"); ③
let date4 = new Date("2000 10 10/00:00:00"); ④
console.log("1:", date1); // 1: Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
console.log("2:", date2); // 2: Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
console.log("3:", date3); // 3: Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
console.log("4:", date4); // 4: Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
< 숫자로 특정 날짜 전달하기 >
year, month, date, hours, minutes, seconds, milliseconds 순서이다.
let date1 = new Date(2000, 10, 10, 0, 0, 0, 0);
let date2 = new Date(2000, 9, 10);
console.log("1:", date1); // 1: Fri Nov 10 2000 00:00:00 GMT+0900 (한국 표준시)
console.log("2:", date2); // 2: Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
Date 객체가 해당 월의 시작을 1이 아니라 0부터 시작하기 때문에 1월은 0, 12월은 11로 전달해야 원하는 출력 결과를 얻을 수 있다.
< 타임스탬프로 날짜 생성하기 >
문자열이나 객체보다 저장 공간을 훨씬 적게 차지하여 빠른 비교와 탐색이 가능하다.
let date = new Date(2000, 9, 10);
let timeStamp = date.getTime(); // ①
console.log(timeStamp); // 971103600000
let dateClone = new Date(timeStamp); // ②
console.log(dateClone); // Tue Oct 10 2000 00:00:00 GMT+0900 (한국 표준시)
{ Date 객체에서 날짜 요소 얻기 }
< getFullYear >
- getYear 메서드는 사용하지 않는다.
let date = new Date(2000, 9, 10);
console.log(date.getFullYear()); // 2000
< getMonth >
let date = new Date(2000, 9, 10);
console.log(date.getMonth()); // 9
- 월을 0부터 11까지 사이의 숫자로 반환하면서 표기는 1월부터 12월로 하니 주의하자.
< getDate >
let date = new Date(2000, 9, 10);
console.log(date.getDate()); // 10
< getDay >
- 0부터 6으로 표현되는 요일을 반환한다. 0은 항상 일요일이며, 6은 토요일이다.
let date = new Date(2000, 9, 10);
console.log(date.getDay()); // 2
< getHours, getMinutes, getSeconds, getMilliseconds >
- 각각 시간, 분, 초, 밀리초를 반환한다.
{ Date 객체의 날짜 요소 수정하기 }
< setFullYear >
- Date 객체의 연도를 수정할 때 사용한다.
let date = new Date(2000, 9, 10);
date.setFullYear(2021);
console.log(date); // Sun Oct 10 2021 00:00:00 GMT+0900 (한국 표준시)
< setMonth>
- 객체의 월을 수정할 때 사용한다.
< setDate >
- 객체의 일을 수정할 때 사용한다.
< setHours, setMinutes, setSeconds >
- 각각 시, 분, 초를 수정할 때 사용한다.
let date = new Date(2000, 9, 10);
date.setHours(1);
date.setMinutes(1);
date.setSeconds(1);
console.log(date); // Tue Oct 10 2000 01:01:01 GMT+0900 (한국 표준시)
{ Date 객체 출력하기 }
< toSrting >
- 현재 저장된 시간을 문자열로 반환한다.
- 콘솔에 출력했을 때와 동일한 형태의 문자열이 출력된다.
const today = new Date(2000, 9, 10, 22);
console.log(today.toString()); // Tue Oct 10 2000 22:00:00 GMT+0900 (한국 표준시)
< toDateString >
- 현재의 날짜만 출력하는 메서드이다.
< toLocaleString, toLocaleDateString >
- 현재 우리가 속한 시간대에 맞게 변환된 날짜와 시간을 반환한다.
- 한국은 Asia/Seoul 시간대에 속하기 때문에 ‘오후’처럼 현지화된 반환값을 볼 수 있다.
- toLocaleString은 날짜와 시간 모두 반환하고, toLocaleDateString은 날짜만 반환한다.
const today = new Date(2000, 9, 10, 22);
console.log(today.toLocaleString()); // 2000. 10. 10. 오후 10:00:00
console.log(today.toLocaleDateString()); // 2000. 10. 10.
{ Date 객체 응용하기 }
< n월씩 이동하기 >
function moveMonth(date, moveMonth) { // ①
const curTimestamp = date.getTime(); // ②
const curMonth = date.getMonth(); // ③
const resDate = new Date(curTimestamp); // ④
resDate.setMonth(curMonth + moveMonth); //⑤
return resDate;
}
const dateA = new Date("2000-10-10");
console.log("A: ", dateA); // A : Tue Oct 10 2000 09:00:00 GMT+0900 (한국 표준시)
const dateB = moveMonth(dateA, 1);
console.log("B: ", dateB); // B : Fri Nov 10 2000 09:00:00 GMT+0900 (한국 표준시)
const dateC = moveMonth(dateA, -1);
console.log("C: ", dateC); // C : Sun Sep 10 2000 09:00:00 GMT+0900 (한국 표준시)
④ 변수 resDate를 만들고 새로운 Date 객체를 생성합니다. Date 객체를 만들면서 ②에서 구한 타 임 스탬프값(curTimestamp)을 인수로 전달합니다. 결과적으로 변수 resDate에는 date 객체와 동일한 타임스탬프값이 들어 있는 Date 객체가 저장됩니다.
⑤ 변수 resDate에 저장된 Date 객체에서 setMonth 메서드를 호출해 기존 월에 moveMonth만큼 더 한 월을 새로운 월로 저장합니다. 결론적으로 이 함수를 호출하면 moveMonth만큼 월을 앞으로 또는 뒤로 이동시킵니다.
< 배열에서 이번 달에 해당하는 날짜만 필터링하기 >
function filterThisMonth(pivotDate, dateArray) { // ①
const year = pivotDate.getFullYear();
const month = pivotDate.getMonth();
const startDay = new Date(year, month, 1, 0, 0, 0, 0); // ②
const endDay = new Date(year, month + 1, 0, 23, 59, 59); // ③
const resArr = dateArray.filter(
(it) =>
startDay.getTime() <= it.getTime() && // ④
it.getTime() <= endDay.getTime()
);
return resArr;
}
const dateArray = [
new Date("2000-10-1"),
new Date("2000-10-31"),
new Date("2000-11-1"),
new Date("2000-9-30"),
new Date("1900-10-11")
];
const today = new Date("2000-10-10/00:00:00"); // 오늘은 2000년 10월 10일이라고 가정합니다.
const filteredArray = filterThisMonth(today, dateArray);
console.log(filteredArray);
// 0: Sun Oct 01 2000 00:00:00 GMT+0900 (한국 표준시)
// 1: Tue Oct 31 2000 00:00:00 GMT+0900 (한국 표준시)
③ 이번 달의 가장 늦은 시간은 다음 달 0일의 23시 59분 59초(이번 달의 가장 마지막 날을 의미)로 설정해 구합니다.
④ filter 메서드를 이용해 dateArray에서 이번 달에 속하는 요소만 필터링합니다. 서로 다른 Date 객체를 비교할 때는 getTime 메서드로 타임스탬프를 기준으로 비교합니다. 특정 Date 객체가 더 크다는 것은 이 객체가 더 미래에 있는 시간이라는 뜻입니다.
- 이번 달에 해당하는 날짜가 있는 Date 객체를 필터링하기 위해서는 다음과 같이 2가지 작업이 필요하다.
- 이번 달에서 가장 빠른 시간, 가장 늦은 시간 구하기
- 1번에서 구한 시간 내에 포함되는 Date 객체를 필터링하기
Q. 왜 이달의 가장 늦은 시간이 다음 달 0일 23시 59분 59초일까?
Date 객체에서 date 즉 일을 0으로 설정하면 해당 월 바로 이전 월의 마지막 날을 의미한다. 즉 2000년 10월 0일은 2000년 9월의 마지막 날이다.
5. 비동기 처리
- 오래 걸리는 작업이 종료될 때까지 기다리지 않고 다음 작업을 수행한다.
< 동기와 비동기 >
- 동기: 순차적으로 코드를 실행하는 것
- 비동기: 특정 작업을 다른 작업과 관계없이 독립적으로 동작하게 만드는 것
- 비동기 작업을 위해 함수 setTimeout을 이용한다.
setTimeout(function () { ①
console.log("1번!");
}, 3000);
console.log("2번!");
// 2번!
// 1번!
① 함수 setTimeout은 두 번째 인수로 전달된 시간(밀리초)만큼 기다린 다음, 첫 번째 인수로 전달 한 콜백 함수를 실행합니다. 따라서 이 코드에서는 3초(3000밀리 초)만큼 기다린 다음 콜백 함수를 실행합니다.
다음과 같이 화살표 함수를 이용하면 더 간결하게 작성할 수 있다.
setTimeout(() => { ①
console.log("1번!");
}, 3000);
console.log("2번!"); ②
// 2번!
// 1번!
function orderCoffee(coffee, time) { ①
setTimeout(() => {
console.log(`${coffee} 제조 완료`);
}, time);
}
orderCoffee("달콤한 커피", 4000);
orderCoffee("레몬 티", 2000);
orderCoffee("시원한 커피", 3000);
// 레몬 티 제조 완료
// 시원한 커피 제조 완료
// 달콤한 커피 제조 완료
< 콜백 함수로 비동기 처리하기 >
다음 예제의 함수 double은 1초를 기다린 다음 전달한 인수에 2를 곱해 콘솔에 출력한다.
function double(num) {
setTimeout(() => {
const doubleNum = num * 2;
console.log(doubleNum);
}, 1000);
}
double(10); // 20
만약 함수 double의 결과를 반환하게 하려면 어떻게 해야 할까?
function double(num) {
return setTimeout(() => {
const doubleNum = num * 2;
return doubleNum; ①
}, 1000);
}
const res = double(10);
console.log(res);
// 알 수 없는 숫자
- 오류가 나는 이유는 반환값이 함수 setTimeout에서 인수로 전달한 콜백 함수가 반환하는 게 아니기 때문이다. 함수 setTimeout은 타이머의 식별 번호를 반환한다. 따라서 콘솔에 출력된 알 수 없는 숫자는 인수로 전달한 콜백 함수의 반환값이 아니라 타이머의 식별 번호일 뿐이다.
- 콜백 함수는 함수 setTimeout에서 전달하는 인수일뿐이다. 따라서 콜백 함수의 반환값과 함수 setTimeout의 반환값은 무관하다.
다음과 같이 콜백 함수의 인수로 2를 곱한 결괏값을 전달하면, 간단하게 비동기 작업의 결괏값을 반환값으로 사용할 수 있다.
function double(num, cb) {
setTimeout(() => {
const doubleNum = num * 2;
cb(doubleNum); // ②
}, 1000);
}
double(10, (res) => { // ①
console.log(res);
});
console.log("마지막"); // ③
// 마지막
// 20
① 함수 double을 호출하며 두 번째 인수로 화살표 함수로 만든 콜백 함수를 전달합니다. 콜백 함수는 함수 double의 매개변수 cb에 저장되며, 호출되면 인수로 전달한 값을 콘솔에 출력합니다.
② 비동기 작업이 완료되면 콜백 함수를 호출해 연산의 결괏값을 인수로 전달합니다.
-> 콜백 함수를 이용하면 비동기 작업의 결괏값을 사용할 수 있다.
< 프로미스 객체를 이용해 비동기 처리하기 >
- 진행 단계 3가지 상태
- 대기 상태: 작업을 아직 완료하지 않음
- 성공 상태: 작업을 성공적으로 완료함
- 실패 상태: 작업이 모종의 이유로 실패함
- 프로미스 객체 형태
const promise = new Promise(실행 함수); ①
- 인수로 실행 함수(executor)를 전달하는데, 실행 함수란 비동기 작업을 수행하는 함수를 말한다.
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
console.log("안녕");
}, 500);
});
// 안녕
- 이 함수는 새롭게 생성된 프로미스 객체의 실행 함수이다. 프로미스 객체를 생성함과 동시에 실행되며 2개의 매개변수를 제공받는다. 프로미스 객체는 생성과 동시에 실행 함수를 실행해 콘솔에 “안녕”이라고 출력한다.
- 실행 함수가 제공받는 2개의 매개변수는 다음과 같다.
- resolve: 비동기 작업의 상태를 성공으로 바꾸는 함수
- reject: 비동기 작업의 상태를 실패로 바꾸는 함수
다음은 매개변수 resolve를 호출하여 작업 상태를 성공 상태로 변경하는 예이다.
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("성공");
}, 500);
});
만약 결괏값을 비동기 작업이 아닌 곳에서 이용하려면 다음과 같이 프로미스 객체의 then 메서드를 이용한다.
- then 메서드는 인수로 전달한 콜백 함수의 비동기 작업이 성공했을 때 실행한다.
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
resolve("성공");
}, 500);
});
promise.then(function (res) { ①
console.log(res);
});
// 성공
① then 메서드를 호출하고 인수로 콜백 함수를 전달합니다. 이 콜백 함수는 비동기 작업이 성공했을 때, 즉 실행 함수가 resolve를 호출했을 때 실행됩니다.
-> 실행 함수에서 0.5초 기다린 다음 resolve를 호출하고 인수로 "성공"을 전달한다.
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("실패");
}, 500);
});
promise.then(function (res) {
console.log(res);
});
promise.catch(function (err) { ①
console.log(err);
});
// 실패
① 실행 함수에서 reject를 호출하여 이 작업이 실패했음을 알리고 인수로 “실패”를 전달합니다.
② 비동기 작업이 실패했으므로 then 메서드에 인수로 전달한 콜백 함수는 실행되지 않습니다.
이때 then 메서드에 전달한 콜백 함수는 실행되지 않는다.
작업이 실패했을 때 실행할 콜백 함수를 설정하려면 다음과 같이 catch 메서드를 사용해야 한다.
const promise = new Promise(function (resolve, reject) {
setTimeout(() => {
reject("실패");
}, 500);
});
promise.then(function (res) {
console.log(res);
});
promise.catch(function (err) { ①
console.log(err);
});
// 실패
-> 이처럼 프로미스의 then과 catch 메서드를 이용하면 작업이 성공하거나 실패했을 때 실행할 콜백 함수를 별도로 설정할 수 있다.
Quiz
1. slice 메서드는 원본 배열을 수정하지 않지만, concat 메서드는 원본 배열을 수정한다. ( O / X )
2. indexOf는 자료형이 달라도 같은 값으로 평가한다. (O / X )
3. sort 메서드는 새로운 배열을 반환하는 것이 아니라, 기존 배열 요소를 다시 정렬한다. (O / X )
4. 협정 세계시에서 시간의 시작되는 순간을 말해보자.
5. Date 객체에서 날짜 요소 얻기에서 연도를 반환받기 위해서는 ( ) 메서드를 이용한다.
6. getDay 메서드에서 목요일은 숫자 몇으로 반환될까?
7. Date 객체 출력하는 메서드 중 '현지화'된 날짜와 시간을 모두 반환하는 메서드의 이름을 말해보자.
1. 다음 코드의 결과를 말해보자.
let arr = [1, 3, 5, 7, 1];
console.log(arr.indexOf(7, 9));
2. reduce 메서드를 이용하여 1부터 5까지 더해지는 코드를 짜보아라.
x / x / o / 1970년 1월 1일 0시 0분 0초 / getFullYear / 4 / toLocaleString
-1
let arr = [1, 2, 3, 4, 5];
let result = arr.reduce((acc, item) => acc + item, 0);
console.log(result); // 15
출처: 이정환, 『한 입 크기로 잘라먹는 리액트』, 프로그래밍인사이트(2023), 2-5~2-9장.
Editor: yunseul