Notice
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
Tags
- 쏙쏙들어오는함수형코딩
- 갈길이 멀구나
- 나는 flux좋아...
- 에릭 노먼드
- 김영한쌤
- 에릭노이먼
- 쏙속 들어오는 함수형코딩
- 출처 : 한입크기로 잘라먹는 타입스크립트
- 출처 : 자바스크립트 딥다이브
- 에릭 노이먼
- 이웅모
- 오종택개발자님
- 유틸리티타입은 공식문서 자주 보자
- 함수형 코딩
- 출처는 코딩앙마
- 로버트 C마틴
- 생코님Redux
- 흥달쌤
- 고등애플
- 출처 : 코딩앙마
- 출처는 코딩애플
- 큰돌의 CS책
- 쏙쏙 들어오는 함수형 코딩
- 쏙쏙 들어오는 함수형코딩
- 리엑트를 다루는 기술
- https://product.kyobobook.co.kr/detail/S000001952246
- 자바스크립트 딥다이브
- 클린코드다시읽기
- 출처 : https://www.boostcourse.org/
- 출처 : 코딩애플
Archives
- Today
- Total
흰둥씨의 개발장
[함수형 코딩#7] 얕은 복사 vs 깊은 복사 본문
불변성(Immutable)
ㄴ 상태를 변경하지 않는 것 (<=> 상태가 변경된다는 의미는 "메모리에 할당된 값을 변경하는 모든 행위")
ㄴ 불변성을 지키지 않는 예시 ) 전역변수 남용해서 어디서 값이 바뀐지 모르는 상황, 변수에 재할당, etc...
ㄴ 불변성을 지키지 않을 때 함수형 프로그래밍에서 일어나는 일 : 야근, 예기치 못한 동작(에러, 실행...등), 의도치 않은 상태 변화...
ㄴ 불변성을 지키는 것으로 "참조에 의한 전달"로 변경될수 있는 자료형의 변화를 쉽게 감지할수 있어 예기치못한 상황을 막을수 있음
변수는 메모리 주소를 가지고 있고, 변수의 선언은 값이 할당될 공간을 마련하는 것
(변수는 값을 저장하기 위한 공간 자체, 메모리 공간을 식별하기 위해 붙인 이름)
=> 변수에 값을 할당하면 "변수가 가리키는 메모리주소에 가서 할당된 값을 찾을 수 있다"
copy를 알기위해 먼저 원시 타입과 객체(참조) 타입의 차이에 대해 알 필요가 있음
구분 | 원시타입 (Primitive type) | 객체(참조)타입 (Object/reference type) |
정의 | 변경 불가능한 값 (immutable value) | 변경 가능한 값 (mutable value) |
변수에 할당하면 ? | 확보된 공간에 실제 값이 저장되는 것처럼 동작 | 확보된 공간에 참조 값이 저장됨 |
해당 타입이 할당된 변수를 다른 변수에 할당하면? |
값에 의한 전달 (원본의 원시 값이 복사되어 전달됨) |
참조에 의한 전달 (원본의 참조값이 복사되어 전달됨) |
자료형 | - string - number - boolean - undefined - Symbol - null - BigInt(ES11에서 추가됨) |
- Array - Object - function - etc... |
해당 타입을 갖는 변수 값을 변경하려면? |
재할당 외에는 방법이 없음 | 재할당 없이 직접 변경가능 (동적추가, 갱신, 삭제 가능) |
//원시타입 예시
let myRank = 1; //원시타입(숫자)을 변수에 할당 =>메모리 주소1에 값 저장
let yourRank = myRank; //다른 변수에 복사! =>메모리 주소 1에 있던 값을 복사(yourRank도 같은 원시값 참조하다가)
yourRank = 2; //재할당 =>재할당 시점에 새로운 값을 새로운 메모리 주소 2에 저장
console.log(myRank); //1 결과적으로 복사본 변경되어도, 원본이 변경되지 않음
console.log(yourRank); //2
//yourRank가 myRank를 복사할 때는 같은 원시값을 복사(참조)하다가,
//yourRank에 재할당이 일어나면 새로운 메모리주소에 값이 저장되어 myRank는 변경 없음
//=> 값에 의한 전달(Call by value)
//원시타입 예시2
function test (number){
return number += 100;
}
let num = 0; //원시값
console.log(num);//0 원본그대로
test(num); //100 원시타입 인수는 값자체가 복사되어 매개변수에 전달되기 때문에
console.log(num);//0 원본 훼손되지 않음
//객체 타입 예시
let obj = { a : 1, b : 2 }; //객체 타입을 할당, 메모리 주소1에 저장
let objCopy = obj; //objCopy도 메모리 주소1을 복사해 감
//=> 참조에 의한 전달(Call by reference)
objCopy.name = "Zzanggu"; //복사본의 값을 변경하면,(프로퍼티 값 동적 생성)
console.log(objCopy); //{a: 1, b: 2, name: 'Zzanggu'} 복사본만 변경되는 것이 아니라
console.log(obj); //{a: 1, b: 2, name: 'Zzanggu'} 원본도 변경됨
//객체 타입 예시2
function test(obj){
return obj.name = '흰둥이';
}
let myPet = { name : '짱구' };
console.log(myPet); //{name: '짱구'}
test(myPet); //객체 타입 인수는 참조값이 복사되어 매개변수에 전달되기 때문에
console.log(myPet); //{name: '흰둥이'} 참조값을 통해 객체 변경할 경우 원본이 훼손됨 => 부수효과 발생
구분 | 방어적 복사 (=deep copy) | 카피온라이트(=shallow copy) |
언제 써야 할까? | 신뢰할 수 없는 코드와 데이터 주고 받을 때 | 통제 가능한 데이터 쓸 때 |
동작 | 객체에 중첩되어있는 객체까지 모두 복사함 | 1depth만 복사함 객체의 경우 참조값을 복사 |
예시 | 원시 값을 할당한 변수를 다른 변수에 할당하는 것, 객체를 복사할때는 아예 새로운 객체를 생성해서 복사하기 |
객체를 할당한 변수를 다른 변수에 할당하는 것 |
목적 | (안전지대의 경계에서) 신뢰할수 없는 데이터는 원본이 변경되는 것을 막자 (얕은 복사보다 리소스 좀더 듦) |
(안전지대 안에서) 통제 가능한 데이터는 원본 보존되니까 리소스를 적게 쓰자 |
원칙 | 1. 데이터가 안전한 코드에서 나갈때 복사하기 2. 안전한 코드로 데이터가 들어올때 복사하기 (tip. 신뢰할 수 없는 데이터를 받아오는 동작을 분리해서 검증하자 / JS에서 구현시 Lodash library 추천함) |
1. 바꿀 데이터의 얕은 복사 2. 복사본을 변경함 3. 복사본을 리턴함 |
깊은복사와 얕은 복사의 차이점 |
모든 레벨을 새롭게 복사하기 때문에 내부에 참조데이터가 있어도 원본과 복사본의 상태가 공유 되지 않음 즉, 원본의 내부에 있는 참조데이터 값 하나를 변경하더라도 복사본 내부의 해당값에 영향이 없음 |
첫 번째 레벨만 복사하기 때문에 내부에 참조데이터가 있는경우 원본과 복사본의 상태가 공유 됨 즉, 원본의 내부에 있는 참조데이터 값 하나를 변경하면 복사본 내부의 해당값도 변경됨 |
객체 깊은 복사
//JSON.parse()JSON.stringify() 사용하기
//아래 방법은 쉽지만, 내부에 함수, undefined, Symbol 등의 타입이 포함되어있다면 제대로 복사되지 않음
let obj = { a: 1, b: 2 };
let objCopy = JSON.parse(JSON.stringify(obj));
objCopy.c = 3
console.log(obj === objCopy) //false
//재귀함수 사용하기
let MY = { a: 1, b: { c: 2 } };
function Copycat (OBJ) {
let res = {};
for(let key in OBJ){
typeof OBJ[key] === 'object' ? res[key] = Copycat(OBJ[key]) : res[key] = OBJ[key];
}
return res;
}
let copy = Copycat(MY);
console.log(copy === MY); //false
console.log(copy); //{ a: 1, b: { c: 2 } };
배열의 깊은 복사
//JSON.parse()JSON.stringify()사용하기
//아래 방법은 배열이나 객체 내부에
//함수, undefined, Date 객체, RegExp 객체 등
//JSON으로 변환할 수 없는 값을 포함하지 않을 때만 사용가능
const original = [1, 2, 3, [4, 5]];
const copy = JSON.parse(JSON.stringify(original));
//재귀함수사용하기
function deepCopy(obj) {
// 기본 타입이거나 null이면, 그대로 반환
if (obj === null || typeof obj !== "object") {
return obj;
}
// 배열인 경우
const copy = Array.isArray(obj) ? [] : {};
// 객체 또는 배열일 경우
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]); // 재귀적으로 복사
}
}
return copy;
}
const original = [1, 2, 3, [4, 5],{name: "흰둥이"}];
const copy = deepCopy(original);
console.log(copy); // [1, 2, 3, [4, 5],{name: "흰둥이"}];
console.log(original === copy); // false
console.log(original[3] === copy[3]); // false
console.log(original[4] === copy[4]); // false
객체 얕은 복사
//Object.assign()
const 점심차려이각박한세상속에서 = { name : '하하' };
const 정발산기슭곰발냄새 = Object.assign({}, 점심차려이각박한세상속에서); //새로운 객체를 생성해버리면 됨
console.log(점심차려이각박한세상속에서 === 정발산기슭곰발냄새); //false
===================================
let original = { a: 1, b: { c: 2 } };
const copy = Object.assign({}, original);
original.b.c = 5;
console.log(copy.b.c); // 5
console.log(original.b === copy.b); // true 내부에 참조데이터값이 있는경우 원본과 복사본 상태공유됨
console.log(original === copy); //false
//스프레드 연산자
const myName = { name : 'cho seongjin' }
const yourName = { ...myName };
console.log(myName === yourName); //false
=====================
const original = { a: 1, b: { c: 2 } };
const copy = { ...original };
copy.a = 3;
copy.b.c = 4;
console.log(original); // 카피본 값만 바꿔도 원본값도 동일하게 변경됨 { a: 1, b: { c: 4 } }
//할당연산자
const 점심차려이각박한세상속에서 = { name : '하하' };
const 정발산기슭곰발냄새 = 점심차려이각박한세상속에서;
console.log(점심차려이각박한세상속에서 === 정발산기슭곰발냄새); //true
배열의 얕은 복사
//스프레드연산자
const original = [1, 2, 3];
const copy = [...original];
//slice()하기
const original = [1, 2, 3];
const copy = original.slice();
==========
//slice()했을 때 내부에 참조데이터가 있다면 원본과 상태가 공유됨
let arr = [ { name : '조', age: 100 }, { name : '김', age: 100 } ];
let arrCopy = arr.slice(); //얕은 복사
console.log(arrCopy); //[{name: '조', age: 100}, {name: '김', age: 100}]
arrCopy[0].age = 30; //복사본 변경
console.log(arr); //{name: '조', age: 30}, {name: '김', age: 100}]
//원본에 복사본의 변경사항이 반영됨
//concat()하기
const original = [1, 2, 3];
const copy = original.concat();
//from()하기
const original = [1, 2, 3];
const copy = Array.from(original);
'함수형 프로그래밍 > 쏙쏙 들어오는 함수형 코딩' 카테고리의 다른 글
[함수형 코딩#18] 반응형 구조 & 어니언 구조 (0) | 2023.07.16 |
---|---|
[함수형 코딩#17] 타임라인 조율하기 (0) | 2023.07.14 |
[함수형 코딩#16] 타임라인 사이에 자원 공유하기 (0) | 2023.07.14 |
[함수형 코딩#15] 타임라인 격리/ JS thread (0) | 2023.07.13 |
[함수형 코딩#14] 중첩데이터에 함수형도구 사용 (0) | 2023.07.12 |