우리는 자바스크립트에서 값을 할당할 수 있다는 것을 압니다. 이미 할당된 변수에 다른 변수의 값을 재할당할 수 있다는 것도 알죠.
하지만 객체의 경우 메모리 상에서 참조(Reference) 된다는 것도 알았습니다.
let o1 = {
name: 'yeony'
}
let o2 = o1
console.log(o1 === o2) // true
언뜻 보면 객체를 복사한 것처럼 보이지만 둘은 서로 같은 객체죠. 바로 같은 메모리 주소를 참조하고 있기 때문입니다.
이번 글에서는 참조되지 않는 복사를 한 번 알아봅시다.
자바스크립트에서 할 수 있는 복사는 크게 두 종류로 나뉩니다. 얕은 복사와 깊은 복사입니다.
얕은 복사는 중접된 객체의 한 단계까지 복사하고, 깊은 복사는 중접된 객체까지 모두 복사합니다.
얕은 복사는 참조된 값(객체의 메모리 주소)를 복사해 같은 객체를 참조하게 됩니다.
얕은 복사는 3가지 방법이 있습니다.
const obj = {
objValue: 'objValue',
nested: {
key: 'value'
}
}
위 객체를 기준으로 살펴봅시다.
spread라는 단어의 의미는 펼치다, 퍼트리다입니다. 즉 전개하는 연산자입니다.
...
이렇게 점 3개를 연달아 작성하는 연산자인데요. ES6에 도입된 문법입니다. 이 연산자를 사용하면 객체의 프로퍼티를 펼쳐서 복사하는 것입니다. 객체와 배열에서 사용할 수 있습니다.
const obj = {
objValue: 'objValue',
nested: {
key: 'value'
}
}
const spread = {
...obj
}
// 서로 다른 객체
console.log(obj === spread) // false
// obj의 중접 객체의 프로퍼티 값 변경
obj.nested.key = 'valueChanged'
// spread의 중첩 객체 프로퍼티 값이 같이 변함
console.log(obj.nested.key === spread.nested.key) // true
복사된 객체와 원본 객체는 서로 다른 객체입니다. =
연산자로 할당했을 때 객체가 서로 같았던 것과는 확실히 다릅니다.
그럼에도 중첩된 객체의 값은 여전히 서로 참조되어 같이 움직입니다.
그렇다면 spread 객체에 새로운 프로퍼티를 추가해보면 어떻게 동작할까요?
spread.spreadOnly = 'onlySpreadValue'
console.log(spread) // objValue: 'objValue', nested: {key: 'valueChanged', key2: 'value2'}, spreadOnly: 'onlySpreadValue!'
console.log(obj) // objValue: 'objValue', nested: {key: 'valueChanged', key2: 'value2'}
spread 객체에만 값이 들어갔습니다! 앞서 말했듯 두 객체는 서로 다른 객체이지만, 중첩된 객체에 같은 참조를 가지고 있기 때문에 이러한 결과가 나온 것입니다.
나머지를 가져오는 방식입니다. 스프레드 연산자와 유사하게 생겼지만 엄연히 다릅니다.
rest는 객체, 배열, 함수의 파라미터에서 사용할 수 있습니다.
const obj = {
objValue: 'objValue',
nested: {
key: 'value'
}
}
const { objValue, ...rest } = obj
console.log(rest) // nested: {key: 'value'}
// obj의 중접 객체의 프로퍼티 값 변경
obj.nested.key = 'valueChanged'
console.log(rest.nested.value) // 'valueChanged'
objValue
를 제외한 나머지 프로퍼티들이 복사되었고, 중첩된 객체의 값은 여전히 참조되어 있는 것을 확인할 수 있습니다.
Object.assign
은 자바스크립트의 내장 메소드입니다.
Object.assign(타깃, 원본 객체)
로 작성하면 얕은 복사된 객체를 돌려줍니다.
const obj = {
objValue: 'objValue',
nested: {
key: 'value'
}
}
const assigned = Object.assign({}, obj)
console.log(obj === assigned) // false
// obj의 중접 객체의 프로퍼티 값 변경
obj.nested.key = 'valueChanged'
console.log(assigned.nested.value) // 'valueChanged'
역시나 중첩된 객체 값은 참조되어 있습니다.
얕은 복사는 중첩된 객체의 경우 계속 참조되는 것을 보았습니다.
반면 깊은 복사는 얕은 복사와는 달리, 원본과 복사본 중 어떤 값을 바꾸어도 서로에 영향이 없다는 것을 보증합니다.
자바스크립트에서 깊은 복사를 생성하는 방법은 한 가지입니다.
아직은 낯선 개념이겠지만 언급하고 넘어갑니다.
JSON은 JavaScript Object Notation인데, 이후에 자세히 알아보도록 하겠습니다.
JSON.stringify()
는 이름에서 유추할 수 있듯이 자바스크립트 값 혹은 객체를 JSON 문자열로 변환합니다.
JSON.parse()
는 JSON 문자열의 구문을 **분석(parse)**하고 그 결과를 바탕으로 자바스크립트 값이나 객체를 생성합니다.
const obj = {
objValue: 'objValue',
nested: {
key: 'value'
}
}
JSON.stringify(obj) // '{"objValue":"objValue","nested":{"key":"value"}}'
const deepCopy = JSON.parse(JSON.stringify(obj))
// obj의 중접 객체의 프로퍼티 값 변경
obj.nested.key = 'valueChanged'
// 변하지 않은 deepCopy의 중첩 객체 값
console.log(deepCopy.nested.value) // 'value'
원본 객체를 수정하였지만 deepCopy 객체의 값은 아무것도 달라지지 않았습니다.
다음 글에서는 함수를 알아보겠습니다.
3 Ways to Shallow Clone Objects in JavaScript
Shallow Copy vs Deep Copy in JavaScript
◾ 자바스크립트의 메모리 👈 이전 글 보기
◾ 함수 👈 다음 글 보기