객체 : 참조에 의한 객체 복사

라용·2022년 11월 12일
0

모던 JavaScript 튜토리얼 내용을 정리 요약한 내용입니다. 더 자세한 설명은 원문 링크를 참고하세요.

생각

객체나 배열은 값을 담고 있지 않아도 값이 true 가 나온다.(배열도 객체) 아마 이 참조에 의한 복사로 객체의 값이 표현되기 때문이 아닐까 싶다. 비어있는 객체는 그 자체로 존재하는 것이고, 이후에 값이 보이더라도 그건 주소값만 참조하는 것이므로 빈 객체는 빈 객체로 존재하는 것.. 맞나? 아무튼 객체를 다른 변수에 지정하면 같은 참조값을 가르키기 때문에 이를 수정하려면 복사를 제대로 해주어야 한다. 지난번에 json 목데이터를 수정하는데 _.cloneDeep() 을 사용한다고 해서 신기하게 봤었는데, 그 내용이 여기 떡하니 나온다. 깊은 복사는 lodash _.cloneDeep()을 쓰자!


정리

객체는 참조에 의해 값이 저장되고 복사됩니다. 어떤 변수에 객체를 할당하면 그 값은 변수가 아닌 메모리 내 어딘가에 저장되고, 변수에는 그 메모리의 주소, 객체에 대한 참조 값을 저장합니다. 따라서 객체가 할당된 변수를 복하면 객체의 참조 값이 복사되고 객체는 복사되지 않습니다. 아래와 같이 객체를 다른 변수로 지정해도, 해당 객체의 참조값만 복사하기 때문에 실제는 하나의 객체 값만을 다루게 됩니다.

let subject = { name: 'Math' };
let test = subject;

// subject 와 test 는 참조값을 통해 하나의 객체를 가르킴

test.name = 'English'
alert(subject.name) // English 출력

객체를 서랍장에 비유하면 변수는 서랍장을 열 수 있는 열쇠와 같습니다. 서랍장은 하나지만 서랍장을 열 수 있는 열쇠는 여러개가 될 수 있습니다.

동일한 객체의 경우 비교 시 일치, 동등 연산자는 동일하게 동작하며, 독립된 객체일 경우에는 비어 있더라도 비교시 거짓을 반환합니다.

// a 와 b 가 동일한 객체를 참조

let a = {};
let b = a; // 참조에 의한 복사

a == b // true
a === b // true

// a 와 b 가 독립된 두 객체일 경우

let a = {};
let b = {};

a == b // false

객체가 할당된 변수를 사하면 동일한 객채에 대한 참조 값이 하나 더 만들어집니다. 만약 해당 객체를 복제해 독립된 객체를 만들고 싶다면 몇가지 방법이 있습니다. (실제로 객체를 복제할 일은 거의 없음, 참조에 의한 복사로 해결하는 일이 대다수)

우선 새로운 객체를 만들고 기존 객체의 프로퍼티를 순회해 원시 수전까지 프로퍼티를 복사할 수 있습니다.

let user = {
    name: "John",
    age: 30,
}

let clone = {};

for(let key in user){
    clone[key] = user[key];
}

Object.assign 을 사용할 수 도 있습니다. 해당 문법은 아래와 같습니다.

Object.assign(dest, [src1, src2, src3 ...])

// dest - 목표로 하는 객체
// srcN - 복사하려는 객체
// 객체 src1 의 프로퍼티를 dest 에 복사하고 dest 를 반환

let user = { name: 'John' };

let grade = { level: 1 };
let class = { color: blue };

// 아래처럼 작성하면 user 배열에 해당 값들이 병합됨

Object.assign(user, grade, class)

user // { name: 'John', level: 1, color: blue }

이때 목표 객체에 동일한 이름을 가진 프로퍼티가 있으면 기존 값이 덮어씌워 집니다.

let user = { name: 'John' };

Object.assign(user, {user: 'Pete'})

user.name // Pete

Object.assign 을 사용하면 반복문 없이 객체를 복사할 수 있습니다.

let user = {
    name: "John",
    age: 30,
};

let clone = Object.assign({}, user);
// user 의 모든 프로퍼티가 빈 객체에 복사되고 clone 변수에 할당

중첩 객체를 복사할 경우 객체 자체는 독립되지만 중첩된 객체는 같은 참조 값을 복사하게 됩니다.

let user = {
    name: "John",
    sizes: {
        height: 182,
        width: 50,,
    }
};

let clone = Object.assign({}, user);

user === clone // false
user.sizes === clone.sizes // true

이 문제를 해결하려면 user[key] 의 각 값을 검사하면서, 그 값이 객체인 경우 객체의 구조도 복사하는 반복문을 상요해야 합니다. 이런 방식을 깊은 복사, deep cloning 이라고 합니다. 표준 알고리즘 Structed cloning algorithm 을 상요하면 위 사례와 비슷한 다양한 상황에서 객체를 복제할 수 있고, 자바스크립트 라이브러리 lodash 메서드인 _.cloneDeep(obj)를 사용하면 알고리즘 구현없이 깊은 복사를 처리할 수 있습니다.

// lodash 

let obj = [{'a':1}, {'b':2}];

let deep = _.cloneDeep(objects);

deep[0] === obj[0] // false

객체는 참조에 의해 할당되고 복사됩니다. 변수에 저장되는 것은 객체 자체가 아니라 객체가 저장된 메모리 상의 주소인 참조입니다. 따라서 객체가 할당된 변수를 복사하거나 함수의 인자로 넣을 때 들어가는 것은 객체가 아닌 객체의 참조 값입니다. 이렇게 복사된 참조 값은 하나의 객체를 가르키기 때문에 해당 참조 값으로 진행한 프로퍼티 추가, 삭제등은 동일한 객체를 대상으로 진행됩니다. 객체의 진짜 복사본을 만들고 싶다면, 얕은 복사(중첩객체가 없는 경우)는 Object.assign 으로 처리하고 깊은 복사는 lodash 의 _.cloneDeep(obj)를 사용합니다.

profile
Today I Learned

0개의 댓글