지난 포스팅 immutability 에 대한 정리 2편 에서는 Object Data type 같은 경우 어떻게 하면 immutability (불변성) 을 지킬수 있는지 방법을 알아보았고 함수 Object.assign, Array.prototype.concat 에 대해 알아보았고 Nested Object (중복 객체) 의 경우 어떻게 대처하면 되는지에 대해 알아보았습니다.
이번 포스팅에서는 함수 와 관련된 immutability (불변성) 에 대해 알아보겠습니다.
fn함수 - 매개변수에 person 이라는 이름으로 받아주고 함수 안에서 person.name 의 값에 "Lee" 라는 문자열을 넣어줍니다.
그리고 fn 함수에 인자로 f1 변수를 인자에 넣고 돌리게 되면 f1의 name 속성의 값은 "Lee" 가 되게 됩니다.
immutability (불변성)에 어긋나게 되죠.
이유는 매개변수로 받은 person에서 f1의 값을 받은게 아니라 주소로 받은것이기 때문에 f1 과 person 변수는 같은 객체를 가리키게 됩니다, 그래서 함수 내 매개변수로 받은 person의 객체에서 name 의 값을 바꾸게 되면 f1 의 객체 속성인 name도 변하게 됩니다.
이제 함수 매개변수에 객체를 넘겼을때 어떻게 하면immutability을 지킬수 있을까요?
Function Immutability- 1
첫번째 방법으로는 함수 내에서 Object.assign 를 사용하여 person 매개변수를 재정의 할것입니다.
아까와 같은 방식으로 Object.assign 함수를 사용하여 o1 객체를 복사해서 o2 변수에 넣어줍니다.
그리고 o2.name을 바꾸고 이번엔 o2.score.concat()을 하여 배열을 새로운 배열로 만들어 복제하여 o2.score에 대입하여 줍니다.
그 뒤o2.score.push(3)을하게 되면 o1,o2의score 객체는 서로 다르기 때문에o1.score의원본 객체가
변하지 않습니다.
왜 Concat을 사용하는가?
왜 concat을 사용하는가에 대한 의문이 들 수도 있습니다.
Object.assign 함수를 사용하면 되는데 왜 굳이 concat 함수를 사용하는 것인가 에 대한 질문에 대하여 설명을 하자면 Object.assign을 사용하게 되면 배열에서 객체가 되기 때문에 배열 고유의 함수들을 사용하고 배열로 쓸려면 Array.prototype.concat()을 사용해야 합니다.
이번 포스팅에선 객체를 대입할 때 어떻게 하면 깊은 복사를 하여 immutability (불변성)을 유지하며 Object.assign 함수를 쓰며, 또한 그 함수 (Object.assign)를 쓰더라도 Nested Object 일 경우엔 concat 함수를 써야 된다는 부분에 대해 알아보았습니다.
다음 포스팅에선 함수로 객체를 넘길 때 생기는 문제점에 대해 알아보고 시간이 남는다면 Object freeze에 대해서도 알아보겠습니다.
프로그래밍을 공부하다보면 shallow copy(얕은 복사) deep copy(깊은 복사)를 경험하신적이 있으실것입니다. c언어 에서는 포인터, java에서는 reference variable 등등 을 관리하고 데이터 처리를 할때 많이 만나게 됩니다. 대표적인 예를 javascript 에서 확인해보겠습니다.
Example ) Shallow Copy - 1
// A 라는 사람이 메모장을 쓴다고 했을때 가정을 해봅시다.
const memoA = { author: 'Person A', content: 'plan to make a coffee' };
// 그리고 B 라는 사람이 A 라는 사람의 메모장을 복사하여 받은 뒤 수정한다고 해봅시다.
const memoB = memoA;
// B라는 사람은 author, content 의 내용만 지우고 B 사람의 이름과 메모를 적습니다.
memoB.author = 'Person B';
memoB.content = 'take a bus';
// memoA의 값 까지 바뀌는 현상이 발생 했습니다.
console.log('memoA, ', memoA); // memoA, {author: 'Person B', content: 'take a bus'}
console.log('memoB, ', memoB); // memoA, {author: 'Person B', content: 'take a bus'}
// memoA 와 memoB가 같다고 나옵니다.
console.log(memoA === memoB) // true
// 이러한 현상을 reference copy (주소 복사), shallow copy(얕은 복사) 라고 하는데 왜 이런 문제가 발생할까요?
const aVar = 10;
상수로 aVar 데이터를 만들고 10의 값을 넣는다면 다음과 같이 aVar 변수 안에 10이라는 데이터가 존재합니다.
하지만 reference 데이터들인 Object.. Array.. 인 경우에는 다르게 동작을 합니다.
const memoA = {author: 'Person A', content: 'Plan to make a coffee'};
바로 객체의 주소를 가진다는 점입니다.
자바로 예를 든다면 참조 변수, 참조 한다고 합니다
const memoB = memoA;
그래서 만약 memoB 변수를 만들고 memoA 변수를 바로 대입을 하여 복사를 한다면 shallow copy (얕은 복사) 가 되어버립니다.
memoB.author = 'Person B';
memoB.content = 'Take a bus';
만약 이 상황에서 memoB의 객체 프로퍼티의 값을 변경한다면 어떻게 될까요?
memoB의 객체 프로퍼티 값들이 변경 되면서 memoA 또한 같은 주소를 가지고 있기 때문에 memoB만 변경을 했을뿐인데 memoA 까지 데이터가 변하게 된 이유 입니다.
이걸 방지하기 위해선 Deep Copy (깊은 복사) 를 해줘야 합니다.
Example ) Shallow Copy- 2
const memoA = { author: 'Person A', content: 'plan to make a coffee' };
const memoB = {...memoA};
memoB.author = 'Person B';
memoB.content = 'take a bus';
console.log('memoA, ', memoA); // memoA, {author: 'Person A', content: 'plan to make a coffee'}
console.log('memoB, ', memoB); // memoB, {author: 'Person B', content: 'take a bus'}
console.log(memoA === memoB) // false
console.log(memoA.author === memoB.author) // false
console.log(memoA.content === memoB.content) // false
내가 생각한 해결 방법 1. [ ES6 ] Spread Operator (스프레드 연산자) - Deep Copy가 아닙니다.
ES6 에서 나온 Spread Operator 을 사용하여 Deep Copy(깊은 복사)를 구현 할 수 없습니다.
Spread Operator 을 이용한다면 1차원 배열 or 객체만 데이터 복사가 됩니다.
let today = new Date(); // Fri Apr 01 2022 22:39:17 GMT+0900 (한국 표준시)
new Date() 함수 호출을 통해 간단하게 날짜를 받아올수있습니다.
Honey Tip ( 꿀팁 )
today.getTime(); // 1648820357030
이러한 형식으로 getTime(); 함수를 호출하게 되면 지정된 날짜의 시간에 해당하는 숫자 값(밀리 초)을 반환하게 됩니다.
let anyDay = new Date(2022, 3, 1) // 2022.04.01 00:00:00
anyDay = new Date(2022, 3, 1, 23, 59, 59) // 2022.04.01 23:59:59
new Date(2022, 3, 1) 이런식으로 날짜 YYYY-MM-DD 를 적어주면 2022년 4월 1일 00시 00분 00초 나오는걸 볼수 있습니다.
만약 비교를 하기위해 사용하고 싶다면 new Date(2022, 3, 1, 23, 59, 59) // 2022년 4월 1일 23시 59분 59초 이러한 형태로 사용하면 됩니다.
Get Weely ,Monthly First ~ Last (주간, 월간 첫날 ~ 마지막날 구하기)
Get Weekly FirstDay ~ LastDay (주간 첫날부터 마지막날 구하기)
const todayDate = new Date();
let firstDay = null;
let lastDay = null;
let subDay = todayDate.getDay(); // (주간 시작일 일요일) weekly start - sunday
let subDay = todayDate.getDay() - 1; // (주간 시작일 월요일) weekly start - monday
// 만약 오늘이 일요일이라면 subday가 0이 되기때문에 subday를 6으로 맞춰줍니다.
if (todayDate.getDay() === 0) {
subDay = 6;
}
firstDay = new Date(
todayDate.getFullYear(),
todayDate.getMonth(),
todayDate.getDate() - subDay
)
lastDay = new Date(
todayDate.getFullYear(),
todayDate.getMonth(),
todayDate.getDate() + (6 - subDay),
23,
59,
59
);
console.log({firstDay, lastDay}); // {firstDay: 2022.03.27, lastDay: 2022.04.02}
주간 첫날 ~ 마지막날 구하는 코드 입니다.
주간의 첫번째 날 구하는 공식
subDay 변수에 getDay() // 0~6 데이터를 집어넣어서
Date('YYYY,MM,DD')의 DD 영역에 todayDate.getDate() - subDay 를 하여 이번주의 첫날을 구합니다.
만약 subDay = getDay() - 1 을 하게 되면 주간 시작일을 월요일(default sunday)로 만들수 있습니다.
마지막날 구하는 공식
todayDate.getDate() + (6 - subDay) 하여 주간의 마지막 날을 구할수 있습니다.
new Date(getFullYear(), getMonth(), getDate() + (6 - subday), 23, 59, 59) 마지막에 23, 59, 59 를 넣은 이유는 그냥 년, 월, 일을 넣게되면 2022년 4월 2일 00시 00분 00초 가 되기 때문에 23, 59, 59를 하여 2022년 4월 2일 23시 59분 59초 이런식으로 만들어서 데이터를 제대로 받아오기 위함 입니다.