예제1
const user = {
name: "Heropy",
age: 85,
emails: ["email@gmail.com"],
};
const copyUser = user;
console.log(copyUser === user);
user.age = 22;
console.log("user", user);
console.log("copyUser", copyUser);
user
라는 변수에 객체 데이터가 할당되어 있는데 속성으로는 name
age
emails
라는 속성을 가지고 있다. 이는 문자, 숫자, 배열 데이터에 해당된다.user
객체 데이터를 copyUser
라는 변수에 할당하고 있다.copyUser
와 user
가 일치하는지 확인하고 있는데 결과값은 true
를 출력하고 있다. 이는 같은 메모리 주소를 가리키고 있다는 뜻이다.user
의 age
속성을 숫자 데이터 22
로 바꾸었을때 같은 메모리 주소를 가리키고 있는 copyUser
의 age
속성 또한 숫자 데이터 85
에서 22
로 바뀌게 된다.예제2 (얕은 복사, Shallow copy)
const user = {
name: "Heropy",
age: 85,
emails: ["email@gmail.com"],
};
const copyUser = Object.assign({}, user);
console.log(copyUser === user);
user.age = 22;
console.log("user", user);
const copyUser = Object.assign({}, user)
부분을 살펴보자.{}
를 넣어주고, 두 번째 인수로는 user
를 넣어주자. {}
는 대상 객체, user
는 출처 객체에 해당된다. 출처 객체 user
의 속성들을 복사해서 대상 객체 {}
에 담아서 Object.assign()
메소드를 실행해서 copyUser
에 반환한다.{}
는 리터럴 방식의 객체 데이터고 참조형 데이터이기 때문에 만들어질 때 새로운 메모리 주소를 생성한다. 이는 copyUser
가 새로운 메모리 주소에 할당되는 것이므로 copyUser
와 user
를 일치연산자로 비교해 보면 false
를 출력한다. 따라서 user
의 age
속성을 숫자 데이터 22
로 변경해도 copyUser
는 별도의 다른 메모리 주소를 가리키고 있으므로 copyUser
에는 영향을 미치지 않는다. 이것이 복사라는 개념이다.Object.assign()
뿐만 아니라 전개 연산자 ...
로도 복사를 할 수 있는데 const copyUser = {...user}
로 수정해도 복사된 똑같은 결과를 출력한다.Object.assign()
, 전개 연산자(...)
로 복사한 개념은 얕은 복사(Shallow copy) 에 해당된다.
예제3 (얕은 복사)
const user = {
name: "Heropy",
age: 85,
emails: ["email@gmail.com"],
};
const copyUser = Object.assign({}, user);
console.log(copyUser === user);
user.age = 22;
console.log("user", user);
console.log("copyUser", copyUser);
console.log("-----");
console.log("-----");
user.emails.push("pushEmail@gmail.com");
console.log(user.emails === copyUser.emails);
// console.log('user', user)
// console.log('copyUser', copyUser)
user
객체의 emails
속성의 배열 데이터에 push()
메소드를 사용했다. push()
메소드는 배열 데이터의 가장 뒷 부분에 새로운 아이템으로 메소드의 인수 부분을 밀어 넣는다.user
의 emails
속성과 copyUser
의 emails
속성을 비교했을때 true
라는 값이 출력됐다.copyUser
를 만들었는데 어떻게 user
와 copyUser
의 emails
배열 데이터속성을 비교 했을때 true
가 나올 수 있을까? 그 이유는 user
는 객체 데이터, emails
는 배열 데이터로써 둘 다 참조형 데이터이기 때문인데 우리는 user
객체 데이터만 복사(겉)를 했을 뿐이어서 그 안에 들어 있는 속성 emails
의 배열 데이터는 따로 복사 되지 않고 똑같은 메모리 주소를 공유하고 있기 때문이다.user
, copyUser
의 age
속성은 서로 다른 숫자 데이터를 가지고 있지만 emails
배열 데이터 속성은 ['emails@gmail.com', 'pushEmail@gmail.com']
두 개의 아이템을 가지고 있다. 이는 얕은 복사를 통해 표면(겉)만 복사 되었기 때문이다.lodash
패키지의 도움을 받아서 구현해 보자.예제1 (깊은 복사 Deep copy)
import _ from "lodash";
const user = {
name: "Heropy",
age: 85,
emails: ["email@gmail.com"],
};
const copyUser = _.cloneDeep(user);
console.log(copyUser === user);
user.age = 22;
console.log("user", user);
console.log("copyUser", copyUser);
console.log("-----");
console.log("-----");
user.emails.push("pushEmail@gmail.com");
console.log(user.emails === copyUser.emails);
console.log("user", user);
console.log("copyUser", copyUser);
$npm i lodash
를 입력해서 lodash
패키지를 설치한다. 이때 lodash
패키지는 웹 브라우저에서 직접 동작을 할 수 있어야 하기 때문에 개발 의존성에 해당하는 -D
를 붙이지 않는다.main.js
로 돌아와서 설치된 lodash
패키지를 불러오기 위해 최상단에 import _ from 'lodash'
를 작성한다. lodash
에서 추천하는 방식은 import lodash
가 아닌 언더바 기호 _
즉, low dash 를 import
에 작성하는 것이다. 언더바 기호가 하나의 객체 데이터라고 해석할 수 있다.const copyUser =
에 lodash
기능을 사용할 건데 _
기호를 시작으로 _.cloneDeep()
메소드를 사용하는데 그 인수로 user
객체 데이터를 넣어주면 const copyUser = _.cloneDeep(user)
깊은 복사가 완성된다. clone
은 복제, Deep
은 깊은 이라는 뜻으로 깊은 복제가 된다.copyUser
와 user
를 비교했을때 false
값을 확인할 수 있으며 user.age = 22
로 했을때 user
는 22
, copyUser
는 85
로 되어 있으며 이전과는 다르게 user.emails.push
를 했을때 user.emails === copyUser.emails
는 false
값을 나타낸다. 그 증거로 user
와copyUser
를 출력해보면 emails
의 배열 데이터 아이템 개수는 push
된 것을 포함해 2개 이고, copyUser
의 emails
의 아이템 개수는 1개이다.참조형 데이터를 복사할 때 얕은 복사로도 충분히 문제없이 복사가 된다면 Object.assing()
, 전개연산자(...)
를 사용해서 참조형 데이터를 복사해도 된다. 하지만 참조형 데이터가 내부에 또 다른 참조형 데이터를 포함하고 있다면 깊은 복사를 해주는 것이 더 안전한다. 이때 깊은 복사는 순수 자바스크립트로는 구현이 복잡하니 lodash
패키지 도움을 받아서 _.cloneDeep()
이라는 패키지 내부 메소드를 통해 손쉽게 깊은 복사를 하자.
This method is like _.clone
except that it recursively clones value. cloneDeep
메소드는 Deep
이 붙어있지 않은 _.clone
메소드와 유사한데 정확하게는 재귀적인 적인 값을 복사한다.
재귀란 하나의 데이터 안에서 어떠한 내용이 반복적으로 실행되는 것을 의미한다. 쉽게 말해 반복실행이다.
즉, _.cloneDeep()
메소드는 반복실행 하면서 모든 값들을 복제하면서 깊은 복사가 가능해진다는 것이다.
예제1
var objects = [{ a: 1 }, { b: 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// expected output: false
objects
의 참조형 데이터인 []
배열 데이터만 복사한 것이 아닌 그 안에 들어있는 또 다른 참조형 데이터인 객체 데이터 {}
까지 모두 복사했다는 것을 알 수 있다.
얕은 복사 깊은 복사는 항상 헷갈리더라구요 그냥 가끔은 cloneDeep 때리는게 편할때도ㅋㅋ 항상 열심히 하시는 성훈님 멋있습니다!