Develop Note by J.S.

[Javascript] 얕은복사, 깊은복사 본문

Language/Javascript

[Javascript] 얕은복사, 깊은복사

js-web 2023. 6. 20. 09:56
반응형

개발을 하다보면 "Object에 들어있는 원시값만 사용하고싶어!" 또는 "a객체의 메모리를 공유하는 다른 이름의 객체를 만들고 싶어" 등의 목적으로 코딩을 하다보면 원치않는 동작을 할때가 있습니다. 가장 기본적인 것이지만 한번 제대로 공부해놓지 않고 대충 보고 넘어가다보면 항상 문제가 생기게 되어 정리해 보았습니다.

1. 얕은복사 (메모리 복사)

let obj = {};
newObj = obj;

let arr = [];
newArr = arr;

Object와 Array는 1, '1', true(boolean) 등의 리터럴 값이 아닌, 리터럴 값이 저장된 메모리 주소들의 묶음을 새로운 이름으로 재할당하여 사용하는 것입니다. obj와 arr처럼 메모리 주소들의 묶음 자체를 newObj, newArr에 할당하면 얕은복사가 됩니다.

 

2. 깊은복사 (원시값만 복사할 때)

/**
 * 깊은복사 방법
 */
function change_val(arr_arg) { 
    //spread
    let temp = [...arr_arg]; 
    temp.push(4); 
    console.log('arr_arg: ', temp); // [1,2,3]
    return; 

    //new instance
    let temp = new Array(1,2,3); 
    temp.push(4); 
    console.log('arr_arg: ', temp); // [1,2,3]
    return; 

    //new Array
    let temp = new Array(arr_arg); 
    temp[0].push(4); 
    console.log('arr_arg: ', temp[0]); // [1,2,3]
    return; 

    //주의사항: 1depth까지만 메모리 재할당이 되며, 2depth이하 속성은 그대로 메모리를 공유함
}

function main() { 
    let arr = [1, 2, 3];
    console.log("value name, arr before call: ", arr); // 1 ,2, 3 (메모리 번지 ex.0x01) 
    change_val(arr); 
    console.log("value name, arr after call: ", arr); // 1, 2, 3 (메모리 번지 ex.0x02) 
    return;
}

main();

사용중인 Object, Array 변수를 원본의 훼손없이 다른 메모리에 재할당하여 사용하고 싶을때, 즉 깊은복사 방법에는 몇가지 방법이 있습니다. 첫째로 spread 문법인데요 { ...originObj }, [ ...originArr ] 와 같이 '...' 사용할 경우 object 내부의 속성들을 하나씩 순차적으로 빼내어 값에 직접 접근하여 다시 새로운 Object 형태로 만들어줍니다. 또는 new Object , new Array로 새로운 객체 인스턴스를 만드는 방법이 있습니다.

다만 예제에서 주의 사항이라고 적힌 부분을 보시면 1Depth까지만 메모리 재할당이 된다고 기입하였는데요, 깊은복사를 위한 메모리 재할당 방법에서 Array 내부 속성이 리터럴이 값이 아니라 또 다시 Array와 같은 메모리 값인 경우 가장 바깥의(1Depth) Array만 메모리 재할당이 된다는 뜻입니다. 이런 케이스는 개발하면서 많이 접하게 되는 문제입니다. 

 

3. 2Depth 이상의 깊은 복사

/** 
 * dept1, dept2
 */

let originArr = [{a:1},{a:2},{a:3}];

let newArr = [...originArr];
newArr[0].a = 2;


console.log('originArr: ', originArr); // [{a:2},{a:2},{a:3}]
console.log('newArr: ', newArr); // [{a:2},{a:2},{a:3}]


// 귀찮은 방법..
let originArr = [{a:1},{a:2},{a:3}];

let newArr = [{...originArr[0]}, {...originArr[1]}, {...originArr[2]}];
newArr[0].a = 2;


console.log('originArr: ', originArr); // [{a:1},{a:2},{a:3}]
console.log('newArr: ', newArr); // [{a:2},{a:2},{a:3}]

//전체복사

let originArr = [{a:1}, {a:2}, {a:3}];
let newArr = [];

for (let item of originArr) {
	newArr.push({...item});
}

위 예시처럼 먼저 1Depth의 originArr를 깊은 복사 한뒤 newArr[0].a =2;와 같이 object의 단일 속성에 접근하여 재 할당하는 방법이 있습니다. 그리고 가장 아래 전체 복사 처럼 Depth만큼의 반복문을 이용하여 전체 깊은 복사를 하는 방법이 있습니다.

 

* 2Dept 이상 Object의 깊은복사

// 2Depth 이상의 Object 깊은복사

// 방법1 - 재귀로 복사
const originObj = { 
	a: { 
    	aa: {},
        ...
    }, 
    b: { 
    	bb: {},
        ...
    }, 
    c: {
    	cc: {},
        ...
    }
}

function copyObj(obj) {
    const result = {};

    for (let key in obj) {
        if (typeof obj[key] === 'object') {
            result[key] = copyObj(obj[key]);
        } else {
            result[key] = obj[key];
        }
    }

    return result;
}

const newObj = copyObj(originObj)

// 방법2 - JSON.stringify -> JSON.parse 로 깊은복사 하는 방법 (성능이슈 있음)
const newObj = JSON.parse(JSON.stringify(originObj))


// 방법3 - structuredClone 사용
const newObj = structuredClone(copyObj)

structuredClone은 현재 Javascript가 제공하는 기본 내장함수로 가장 간편하고 좋은 깊은복사 방법입니다.

반응형

'Language > Javascript' 카테고리의 다른 글

[Javascript] This  (0) 2023.06.28
[Javascript] 실행 컨텍스트  (0) 2023.06.27
[Javascript] Call by Value, Call by Reference, Call by Sharing  (0) 2023.06.20
[Javascript] Async, Await  (0) 2023.06.19
[Javascript] Promise  (0) 2023.06.19