-
[JavaScript] 참조형(Reference) 데이터의 할당 구조, 깊은복사/얕은복사개발 여정/JavaScript 2022. 10. 22. 11:42
| 참조형(Reference) 데이터의 할당 구조 |
기본형(Primitive) 데이터와 참조형(Reference) 데이터 할당의 차이점
변수영역/데이터 영역 외에 '객체의 변수영역'이 또 추가됨
변수에 참조형(Reference) 데이터 할당할 경우
var obj1 = { a: 1, b: 'ddd' };
참조형 데이터 할당 구조 - 컴퓨터는 우선 변수 영역의 빈 공간(@1002)에 이름(obj1)과 값(데이터 영역 주소) 할당
- 데이터 영역 @5001에 obj1의 값을 위한 공간을 확보
- @5001에 값을 저장할 별도의 데이터 영역(@7103 ~ ?) 확보, @5001에 이 주소 저장
- @7103 및 @7104에 각각 a, b라는 프로퍼티 이름 지정
- 데이터 영역에 숫자 1이 있는지 검색
- 없으면 임의로 @5002에 숫자 1을 저장하고, 이 주소를 @7103에 저장
- 데이터 영역에 문자열 'ddd'가 있는지 검색
- 없으면 임의로 @5003에 'ddd' 저장, 이 주소를 @7104에 저장
불변 객체 (Immutable Object)
- 불변 객체는 최근 React, Vue.js, Angular 등에서나 함수형 프로그램이, 디자인 패턴에서 매우 중요한 개념
- 객체의 사본을 수정하더라도 원본 객체는 변하지 않아야 하는 것을 말함
객체의 가변성에 따른 문제점
var singer = { name: 'Jennie', gender: 'female' }; var changeName = function (singer, newName){ var newSinger = singer; newSinger.name = newName; return newSinger; } var singer2 = changeName(singer, 'Lisa'); if(singer !== singer2){ console.log('가수 정보가 변경되었습니다.') } console.log(singer.name, singer2.name); // Lisa, Lisa (singer2 뿐만 아니라 singer의 name도 바뀜) console.log(singer === singer2); // true (두 객체가 같다고 나와버림)
- changeName 함수에서 newSinger을 singer로 할당하는 순간 singer와 newSinger은 같은 데이터 주소를 갖게 됨
- 따라서 newSinger의 이름을 newName으로 할당받는 순간, singer의 name도 변하게 됨
객체의 가변성에 따른 문제점 해결 방법
var singer = { name: 'Jennie', gender: 'female' }; var changeName = function (singer, newName){ return { name: newName, gender: singer.gener } }; // changeName이 새로운 객체를 반환하도록 수정 -> 다른 데이터 영역 주소를 갖게 됨 var singer2 = changeName(singer, 'Lisa'); if(singer !== singer2){ console.log('가수 정보가 변경되었습니다.') // 가수 정보가 변경되었습니다 콘솔에 반영됨 } console.log(singer.name, singer2.name); // Jennie, Lisa console.log(singer === singer2); // false
함수 changeName이 새로운 객체를 반환하면서, singer와 singer2는 서로 다른 데이터 영역 주소를 갖게 됨.
따라서 singer2의 내부 프로퍼티가 변경되어도 singer에는 영향이 없음.
객체의 복사 : 얕은 복사, 깊은 복사
얕은 복사 (Shallow Copy)
원본 객체에서 프로퍼티를 복사할 때 그 주솟값만 복사하는 방법.
원본과 사본이 동일한 참조형 데이터 주소를 갖게 됨.
var copyObject = function (target) { var result = {}; // 새로운 객체를 할당해서 객체 바로 안의 프로퍼티는 다른 주소 할당 for (var prop in target){ result[prop] = target[prop]; // 객체 안의 중첩객체는 얕은 복사 수행 } return result; } var singer = { name: 'Jennie', descriptions: { nationality: 'Korea' } }; var singer2 = copyObject(singer); singer2.name = 'Lisa'; console.log(singer.name === singer2.name) // false singer2.nationality = 'Thailand'; console.log(singer.descriptions.nationality === singer2.descriptions.nationality) // true
위의 copyObject 함수에서 result라는 새로운 객체를 생성했기 때문에, result 안의 내부 프로퍼티는 다른 데이터 주소를 갖게 되었다.
하지만 객체 안의 객체, 즉 중첩객체에 대해서는 result[prop] = target[prop]라고 그대로 원본 값을 넣도록 했기 때문에,
서로 같은 데이터 주소를 갖게 되었다.
따라서 singer2의 description 값이 바뀌면, singer의 description 값도 바뀌게 된다.
깊은 복사 (Deep Copy)
내부의 모든 값들을 하나하나 찾아서 전부 복사하는 방법.
원본 객체를 불변값으로 사용할 수 있음.
// 중첩객체에 대해 깊은 복사를 수행하는 함수 var copyObject = function (target) { var result = {}; if(typeof target === 'object' && target !== null){ for (var prop in target){ result[prop] = copyObject(target[prop]); // 재귀를 이용한 깊은 복사 } } else { result = target; // 객체 타입이 아닌 경우 target을 그대로 저장 } return result; } var singer = { name: 'Jennie', descriptions: { nationality: 'Korea', songs: ['Shut Down', 'Pink Venom'] } }; var singer2 = copyObject(singer); singer2.name = 'Lisa'; singer2.descriptions.nationality = 'Thailand'; singer.descriptions.songs[1] = 'Lovesick Girls'; console.log(singer) // {name: 'Jennie', descrptions: {nationality: 'Korea', songs: ['Shut Down', 'Lovesick Girls'] } } console.log(singer2) // {name: 'Lisa', descrptions: {nationality: 'Thailand', songs: {0:'Shut Down', 1:'Pink Venom'} } }
재귀를 이용한 깊은 복사를 했기 때문에, 사본의 중첩객체도 다른 데이터 주소를 갖게 되었다.
따라서 독립적으로 값을 할당받게 되며, 원본과 사본은 서로 변경되도 영향을 받지 않게 된다.
※ copyObject의 if문에서 null 체크를 한 것은 typeof 명령어가 null에 대해서도 'object'를 반환하기 때문이다. (JavaScript 자체 버그)
참조
코어 자바스크립트 (정재남 저)
'개발 여정 > JavaScript' 카테고리의 다른 글
JavaScript의 비동기적 동작방식 (Web APIs) (0) 2023.06.03 [JavaScript] 실행 컨텍스트로 살펴보는 호이스팅 (0) 2022.11.04 순수 재귀함수 (Pure Recursion) (0) 2022.10.22 [JavaScript] 기본형 데이터 할당 시 메모리 구조 (0) 2022.10.20 [Javascript] 호이스팅이란? (Hoisting) (0) 2022.10.06