오늘 뭐했냐/개발에 대한 주저리

깊은 복사와 얕은 복사

스스로에게 2023. 6. 13. 10:37

기본형의 경우 값을 복사한 다음 다른 값을 재할당할 경우에 그 식별자가 가리키는 주소값만 변경되어 문제가 없지만 참조형은 다르다 obj를 위한 메모리 공간이 따로 존재하고 만약 obj = obj2를 실행한다면 obj와 obj2는 같은 공간을 공유하게 된다 따라서 obj2의 속성값을 바꿀 경우 obj에 영향을 줄 수 있다

// user 객체를 생성
var user = {
	name: 'mjm',
	age: 25,
};

// 이름을 변경하는 함수, 'changeAge'을 정의
// 입력값 : 변경대상 user 객체, 변경하고자 하는 이름
// 출력값 : 새로운 user 객체
// 특징 : 객체의 프로퍼티(속성)에 접근해서 age를 변경했네요! -> 가변
var changeAge = function (user, newAge) {
	var newUser = user;
	newUser.age = newAge;
	return newUser;
};

// 변경한 user정보를 user2 변수에 할당하겠습니다.
// 가변이기 때문에 user1도 영향을 받게 될거에요.
var user2 = changeAge(user, 35);

// 결국 아래 로직은 skip하게 될겁니다.
if (user !== user2) {
	console.log('유저 정보가 변경되었습니다.');
}

console.log(user.age, user2.age); // 25 25
console.log(user === user2); // true

그래서 우리는 객체를 복사할 때 주소값이 아닌 내용물만 가져오려면 다른 방법이 더 필요하다

 

var changeAge = function (user, newAge) {
	return {
        name : user.name,
        age : newAge,
    } ;
};

일단 기본적인 원리는 객체 안에 프로퍼티가 가리키는 값만을 가져온다는 것이다 

 

//이런 패턴은 어떨까요?
var copyObject = function (target) {
	var result = {};

	// for ~ in 구문을 이용하여, 객체의 모든 프로퍼티에 접근할 수 있습니다.
	// 하드코딩을 하지 않아도 괜찮아요.
	// 이 copyObject로 복사를 한 다음, 복사를 완료한 객체의 프로퍼티를 변경하면
	// 되겠죠!?
	for (var prop in target) {
		result[prop] = target[prop];
	}
	return result;
}

이렇게 반복문을 이용할 수 있다 그러나 객체 안에 또 객체가 있는 중첩 객체라면 

 

var copyObjectDeep = function(target) {
	var result = {};
	if (typeof target === 'object' && target !== null) {
		for (var prop in target) {
			result[prop] = copyObjectDeep(target[prop]);
            // console.log(result)
		}
	} else {
		result = target;
	}
	return result;
}

//결과 확인
var obj = {
	a: 1,
	b: {
		c: null,
		d: [1, 2],
	}
};
var obj2 = copyObjectDeep(obj);

obj2.a = 3;
obj2.b.c = 4;
obj2.b.d[1] = 3;

console.log(obj); // { a: 1, b: { c: null, d: [ 1, 2 ] } }
console.log(obj2); // { a: 3, b: { c: 4, d: { '0': 1, '1': 3 } } }

재귀 함수를 이용할 수 있다 재귀 함수에 대한 내용은 알고 있었지만 return에만 붙여서 사용했었는데 저렇게도 할 수 있다는 생각을 가지고 여러가지 방법에 대해서 인지하고 있어야겠다

 

위에 재귀함수를 나름대로 실행 순서를 정리한 내 생각은 이렇다 

  1. obj에 대한 함수 실행
  2. obj에 대한 함수 일시정지 > b에 대한 함수 실행
  3. obj에 대한 함수 일시정지 > b에 대한 함수 일시정지 > d에 대한 함수 실행
  4. obj에 대한 함수 일시정지 > b에 대한 함수 실행 + d에 대한 함수 종료
  5. obj에 대한 함수 실행 + b에 대한 함수 종료
  6. obj에 대한 함수 종료

 

  1. a 전달 a에 대한 함수 실행 else라서 result = target으로 마치고 다음 b 진행
  2. b 전달 b에 대한 함수 실행 => b는 object네 b에 대해서 반복문 실행
  3. c전달 c에 대한 함수 실행 else라서 result = target으로 마치고 다음 d 진행
  4. b안에 d 진행 => object네 d에 대해서 반복문 실행
  5. b안에 d[0] 복사, d[1]복사 
  6. d 복사 종료 복사한 d 반환
  7. b 종료 => 복사한 b 전체 반환하고 종료
  8. 복사한 obj 전체 반환 종료

 

얕은 복사 : 참조형 변수가 가지는 주소값 묶음(별도의 메모리 공간)을 복사해서 영향을 주고 받을 수 있다

깊은 복사 : 내용물만 가져와서 서로 다른 주소값을 가지고 있어 원본 데이터에 영향을 주지 않는다   

 

의도적으로 얕은 복사를 한 다음 프로퍼티를 추가해서 사용할 수도 있겠지만 깊은 복사로 알맹이만 빼오는 게 더 나을 것 같다 

 

 

'오늘 뭐했냐 > 개발에 대한 주저리' 카테고리의 다른 글

실행 컨텍스트 (스코프, 호이스팅, 렉시컬 환경)  (0) 2023.06.13
null과 undefined  (0) 2023.06.13
데이터 타입과 메모리  (0) 2023.06.12
Set  (0) 2023.05.24
Map  (1) 2023.05.24