본문 바로가기
개인공부/JavaScript

얕은 복사와 깊은 복사

by 왕큰새 2020. 7. 29.
728x90

얕은 복사(Shallow copy)         vs           깊은 복사 (Deep copy)

   바로 아래 단계의 값만 복사                          내부의 모든 값들을 하나하나 찾아서 전부 복사

 

 

 

 

참조형 데이터가 저장된 프로퍼티를 복사할 때, 그 주솟값만 복사  - 얕은 복사

 

해당 프로퍼티에 대한 원본과 사본이 모두 동일한 참조형 데이터의 주소를 가리키게 된다.

사본을 바꾸면 원본도 바뀌고, 원본을 바꾸면 사본도 바뀐다.

 

변수 복사 비교 에서 다뤘던 내용이다. 

 

불변객체 (immutable object)

참조형 데이터의 '가변'은 데이터 자체가 아닌 내부 프로퍼티를 변경할 때만 성립한다.

데이터 자체를 변경하고자 하면(새로운 데이터를 할당하고자 하면) 기본형 데이터와 마찬가지로

기존 데이터는 변하지 않는다. 이럴 때 사용하는 것이, 내부 프로퍼티를 변경할 필요가 있을때, 

매번 새로운 객체를 만들어 재할당하기로 규칙을 정하거나, 자동으로 새로운 객체를 만드는 도구를 활용하면,

불변성을 확보할 수 있을 것이다. 

 

불변성이 필요한 경우? 

user가 이름을 변경하려고 한다. 

바뀌기 전의 정보와 바뀐 후의 정보의 차이를 보여줘야 하는데, 내부 프로퍼티만 변경하면, 

객체는 같은 주소를 바라보고 있다. 이럴 때, 서로 다른 객체를 바라보게 만들어 문제를 해결한다.

 

 

 

기본 정보를 복사해 새로운 객체를 반환하는 함수(얕은 복사)

var copyObject = function (target) {
	var result = {};
    for(var prop in target) {
    	result[prop] = target[prop];
    }
    return result;
};

 

 

CopyObject를 이용한 객체 복사

var user = { 
 	name : 'Sanghyo',
    urls : {
    	portfolio: 'http://github.com/sanghyo95'
         }
    };
    
    
 var user2 = copyObject(user);
 user2.name = 'Jung';
 
 if( user !== user2) {
 		console.log('유저정보 변경 완료');
        }
        
console.log(user.name, user2.name); // sanghyo Jung
console.log(user.name === user2.name); // false

user.urls.porfolio = 'kunsae.tistory.com';
console.log(user.urls.portfolio === user2.urls.portfolio); // true
 

 

user 객체에 직접 속한 프로퍼티 즉, name 은 복사해서 새로운 데이터가 만들어진 반면,

한 단계 더 들어간 urls의 내부 프로퍼티들은 기존 데이터를 그대로 참조하는 것이다. 

이런 현상이 발생하지 않게 하려면 user.urls 프로퍼티에 대해서도 불변 객체로 만들 필요가 있다.

 

 

중첩된 객체에 대한 깊은 복사'

var user2 = copyObject(user);
user2.urls = copyObject(user.urls);

user.urls.portfolio = 'http://portfolio.com';
console.log(user.urls.portfolio == user2.urls.portfolio); // false

 

객체의 프로퍼티 중 그 값이 기본형 데이터일 경우 에는 그대로 복사 하면 되지만,

참조형 데이터는 다시 그 내부의 프로퍼티들을 복사해야 한다.

 

객체의 깊은 복사를 수행하는 범용 함수

1	var copyObjectDeep = function(target) {
2		var result = {};
3	    if(typeof target === 'object' && target !== null) {
4	    	for(var prop in target) {
5	        	result[prop] = copyObjectDeep(target[prop]);
6	            }
7	    } else {
8	    	result = targe;
9	    }
10	   return result;
11	};

3번째 줄에서 target 이 객체인 경우에,  내부 프로퍼티들을 순회하며 copyObjectDeep 함수를 재귀적으로 호출하고,

객체가 아닌 경우, 8번째 줄에서 target을 그대로 지정하게끔 했다.

이 함수를 사용해 객체를 복사한 다음에는 원본과 사본이 서로 완전히 다른 객체를 참조하게 되어 어느 쪽의 프로퍼티를 변경하더라도 다른 쪽에 영향을 주지 않는다.

 

3번째 줄에서 target !== null 조건은 typeof 명령어가 null에 대해서도 'object'를 반환하기 때문 !!

(자바스크립트 자체 버그)

 

간단하게 깊은 복사 처리하는 다른 방법

객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 것.

메서드(함수)나 숨겨진 프로퍼티인 __proto__ , getter/setter 등과 같이 JSON으로 변경할 수 없는 

프로퍼티들은 모두 무시한다. httpRequest로 받은 데이터를 저장한 객체를 복사할 때, 순수한 정보만 다룰때 유용!

 

var copyObjecViaJSON = function (target) {
	return JSOn.parse(JSON.stringify(target));
};

'개인공부 > JavaScript' 카테고리의 다른 글

실행 컨텍스트(execution context)  (0) 2020.08.03
undefined & null  (0) 2020.07.29
변수 복사 비교(기본형, 참조형)  (0) 2020.07.29
불변값 & 가변값  (0) 2020.07.27
메모리와 데이터  (0) 2020.07.27