javascript basics #2 오브젝트의 복사와 참조

Jake Seo·2020년 8월 19일
0

javascript-basics

목록 보기
2/7

javascript basics #2 오브젝트의 복사와 참조

변수는 오브젝트를 참조한다.

자바스크립트에서 primitive 타입(number, string, boolean, undefined, ...)과 오브젝트를 다루다 보면 헷갈리기 쉬운 부분이 바로 복사와 참조이다.

오브젝트는 "참조에 의해(by reference)" 복사되고 저장된다. 반면 primitive 값들은 string, number, boolean"값으로서(by value)" 할당되고 복사된다.

let primitiveExample = "Jake Seo";
let objectExample = {
  name: "Jake Seo"
}

위와 같은 예제가 있을 때,

  • primitiveExample 변수는 값 자체를 저장한다.
  • objectExample 변수는 오브젝트 자체를 저장하지 않고 메모리의 주소를 저장한다. 다른 말로 표현하면 메모리의 주소를 참조한다.

만일, Object자체를 다른 변수에 재할당하면 어떻게 될까?

let objectExample = {
  name: "Jake Seo"
}
let objectExample2 = objectExample;

위와 같이 작성하면 objectExample2는 독립된 새로운 오브젝트가 된다기보다 objectExample이 참조하는 주소를 가리키는 또 다른 변수가 된다는 것에 주의해야 한다.

이는 값을 복사하는 것이 아닌 주소를 복사하는 것이 된다.

같은 주소를 참조하기 때문에 objectExample의 내용이 바뀌면? objectExample2의 내용도 바뀌게 된다.

참조에 의한 오브젝트 비교

자바스크립트에는 두가지의 비교 연산자가 있다.

  • 첫 번째는 ==이다. ==는 오직 값이 같은지만 비교한다.
  • 두 번째는 ===이다. ===는 값 외에 타입도 같은지 비교한다.

간단한 예를 들자면 문자열 '1'과 숫자 1==에서는 true지만 ===에서는 false이다.

오브젝트의 경우에는 이 두 연산자가 어떻게 적용될까?

오브젝트는 오직 두 오브젝트가 같은 주소를 가리킬 때만 비교 연산자에 의해 true값이 출력된다.

위는 서로 다른 오브젝트를 두가지 비교 연산자를 통해 비교한 것인데, 둘 다 false가 나옴을 볼 수 있다.

위는 같은 주소를 참조하게 만든 뒤에 비교 연산자를 통한 비교를 해본 것이다.

그렇다면 Objectprimitive 타입을 비교하면 어떻게 될까? 이 경우에는 Objectprimitive 타입으로 변하게 된다.

primitive 타입인 string과 비교했을 때, .toString() 메소드를 거친 후의 결과가 됨을 알 수 있다.

단일 오브젝트를 독립된 오브젝트 2개로 복사하고 싶다면?

앞서 = 연산자를 통해 변수에 오브젝트를 다시 한번 할당하는 일은 단순히 그 변수에게 오브젝트의 참조 주소만 할당시킨다는 것을 배웠다. 그렇다면 진짜 오브젝트를 복사해서 독립적인 오브젝트 2개로 만들기 위해서는 어떻게 해야 할까?

오브젝트를 순회하며 값을 복사하기

기존 오브젝트 내부에 있는 프로퍼티를 순회하며 새 오브젝트에 값을 옮기는 방식이다.

let newObject = {};

for (let key in originalObject) {
  newObject[key] = originalObject[key];
}

Object.assign() 메소드 이용하기

Object.assign() 메소드란?

공식문서 링크

위와 같은 메소드이다.

모든 enumerable own propertiessource로부터 target으로 복사한 뒤에 target 오브젝트를 반환한다고 되어있다.

enumerable 하다는 표현이 약간 생소할 수 있는데, 오브젝트에는 기본적으로 for ... in 구문을 통해 나오는 부분이 있고, 그렇지 않은 부분이 있다.

위의 경우를 예로 들자면 'key'라는 key는 우리가 추가해줬기 때문에 enumerable 하지만, __proto__ 하위에 속한 오브젝트 기본 내장 메소드들은 우리가 딱히 추가해주지 않아도 built-in으로 존재하는 것들이다. 그런 항목들은 enumerable 하지 않다.

어찌됐든, Object.assign을 사용해보면

위와 같이 복사가 잘 되었다.

그렇다면 주소값만 참조하는 것은 아닌지, 서로 다른 두 개의 주소가 만들어졌는지 확인하기 위해 두 오브젝트 프로퍼티 내부의 값을 조금 바꿔보자.

mecopiedMe의 내용을 조금씩 바꿔서 동시에 출력해본 결과, 서로 다른 주소 값을 갖고 있음이 확인되었다.

오브젝트 안에 오브젝트가 있는 경우에 복사 방법

임시로 company라는 오브젝트 내부에 employee라는 오브젝트가 있는 예제를 만들었다.

얼핏 보기에는 Object.assign() 메소드를 통해 문제없이 오브젝트가 복사된 것처럼 보이지만, 사실 오브젝트 내부의 프로퍼티인 employee가 같은 오브젝트를 가리키고 있다.

기존의 companyemployee가 이직을 해서 이름이 Transferred로 바뀌었다고 해보자.

분명 company 오브젝트의 employee를 수정했는데, newCompany 오브젝트의 employee마저 바뀌어버리는 것을 볼 수 있다.

둘은 같은 주소를 공유하고 있다. 이러한 경우에는 두가지 방법으로 완전한 복사를 할 수 있다.

JSON String으로 변환 후에 다시 JSON으로 변환하는 방식으로 복사하기

위는 JSON.stringify() 메소드를 이용하여 오브젝트를 JSON String 형식으로 바꾼 뒤에 다시 JSON.parse() 메소드를 이용하여 오브젝트를 만들어주는 방식으로 깊은 복사(deep copy) 를 했다.

Lodash 라이브러리를 이용하여 복사하기

Lodash는 자바스크립트를 다루다보면 겪게되는 불편함에 대응하여 만들어진 라이브러리이다. Lodash cloneDeep라는 함수를 제공하여 사용자가 편하게 깊은 복사 를 할 수 있게 해준다.

profile
풀스택 웹개발자로 일하고 있는 Jake Seo입니다. 주로 Jake Seo라는 닉네임을 많이 씁니다. 프론트엔드: Javascript, React 백엔드: Spring Framework에 관심이 있습니다.

0개의 댓글