얕은 복사 (Shallow Copy)
참조값이 같다
= 연산자
// 아예 선언을 따로 한 경우
const obj1 = {value : 10};
const newObj1 = {value : 10};
console.log(obj1 === newObj1); // false - 참조값이 다르다
// = 연산자를 이용해 얕은 복사(객체)
const obj = {value : 10};
const newObj = obj;
console.log(obj === newObj); // true - 참조값이 같다
// = 연산자를 이용해 얕은 복사(배열)
const arr = [1, 2, 3]
const newArr = arr
console.log(arr === newArr) // true - 참조값이 같다
// 참조 값이 같은 경우 원본 데이터(arr)에 데이터를 넣으면 newArr이 영향을 받는다.
arr.push(4)
console.log(newArr) // [1,2,3,4]
깊은복사 + 얕은 복사 (Deep + Shallow Copy)
Object.assign()
https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
Object.assign() - JavaScript | MDN
Object.assign() 메서드는 출처 객체들의 모든 열거 가능한 자체 속성을 복사해 대상 객체에 붙여넣습니다. 그 후 대상 객체를 반환합니다.
developer.mozilla.org
const category = {category: "espresso"}
const category1 = Object.assign({}, category); // false - 참조값이 다르다
const category2 = Object.assign(category, {}); // true - 참조값이 같다
const category3 = Object.assign(category) // true - 참조값이 같다
console.log(category === category1) // false
console.log(category === category2) // true
console.log(category === category3) // true
// 객체의 속성을 변경하였을 경우, 참조값이 다른 경우만 변경되지 않음
category.category = "tibana"
console.log(category1) // {"category":"espresso"}
console.log(category2) // {"category":"tibana"}
console.log(category3) // {"category":"tibana"}
Object.assign(target, source)
target이 같은 경우 해당 target을 덮어쓰고, source값이 같은 경우에도 새로 생성된다.
여기까지만 보면 해당 메서드는 깊은 복사(Deep copy)를 하는 것 같다. 하지만 아래의 경우를 살펴보자.
const obj = {
a: 1,
b: {
c: 2,
},
};
const newObj = Object.assign({}, obj);
console.log(newObj); // {"a":1,"b":{"c":2}}
console.log(newObj === obj); // false 참조값이 다르다
// obj의 하위객체는 다른 참조값을 바라보고 있다.
obj.a = 444;
console.log(newObj); // {"a":1,"b":{"c":2}}
console.log(obj); // {"a":444,"b":{"c":2}}
// 그러나 하위 객체의 하위 객체는 같은 참조값을 바라보고 있다.
obj.b.c = 100;
console.log(newObj); // {"a":1,"b":{"c":100}}
console.log(obj); // {"a":444,"b":{"c":100}}
console.log(newObj === obj); // false
console.log(newObj.b.c === obj.b.c); // true
객체 자체는 다르나, 객체의 하위 객체 속성들은 같은 참조 값을 참조하고 있다.
전개연산자
전개연산자도 마찬가지로 깊은복사 + 얕은 복사 (Deep + Shallow Copy) 이다.
const category = {"category":"espresso"}
const category1 = {...category} // false - 참조값이 다르다
console.log(category === category1) // false
// 객체의 속성을 변경하였을 경우, 참조값이 다른 경우만 변경되지 않음
category.category = "tibana"
console.log(category1) // {"category":"espresso"}
// 그러나 객체의 속성의 속성, 즉 객체 안의 객체는 같은 참조값을 갖는다.
const obj = {
a: 1,
b: {
c: 2,
},
};
const newObj = {...obj}
// obj의 하위객체는 다른 참조값을 바라보고 있다.
obj.a = 444;
console.log(newObj); // {"a":1,"b":{"c":2}}
console.log(obj); // {"a":444,"b":{"c":2}}
// 그러나 하위 객체의 하위 객체는 같은 참조값을 바라보고 있다.
obj.b.c = 100;
console.log(newObj); // {"a":1,"b":{"c":100}}
console.log(obj); // {"a":444,"b":{"c":100}}
console.log(newObj === obj); // false
console.log(newObj.b.c === obj.b.c); // true
깊은 복사(Deep Copy)
깊은 복사는 객체 안에 객체가 있더라도 참조값이 다른 것을 말한다.
1. 재귀 함수를 이용한 깊은 복사
function clone(item) {
if (!item) {
return item;
} // null, undefined values check
var types = [Number, String, Boolean],
result;
// new String('aaa'), or new Number('444') 이런것들은 원시값으로 변환
types.forEach(function (type) {
if (item instanceof type) {
result = type(item);
}
});
if (typeof result == "undefined") {
if (Object.prototype.toString.call(item) === "[object Array]") {
result = [];
item.forEach(function (child, index, array) {
result[index] = clone(child);
});
} else if (typeof item == "object") {
if (item.nodeType && typeof item.cloneNode == "function") {
result = item.cloneNode(true);
} else if (!item.prototype) {
if (item instanceof Date) {
result = new Date(item);
} else {
result = {};
for (var i in item) {
result[i] = clone(item[i]);
}
}
} else {
if (false && item.constructor) {
result = new item.constructor();
} else {
result = item;
}
}
} else {
result = item;
}
}
return result;
}
const obj = {
a: 1,
b: {
c: 2,
},
};
const newObj = clone(obj);
console.log(obj); // {a: 1, b: { c: 2 } }
console.log(newObj); // {a: 1, b: { c: 2 } }
obj.b.c = 100;
console.log(obj); // {a: 1, b: { c: 100 } }
console.log(newObj);// {a: 1, b: { c: 2 } }
2. JSON.stringify()를 이용한 복사
JSON.stringify()은 JSON을 문자열로 변환하는데 이 과정에서 원본 객체와 참조가 모두 끊어진다.
그리고 JSON.parse()를 이용해 다시 JSON객체로 만들어주면 깊은 복사가 된다.
하지만 이 방법은 다른 방법에 비해 느리다.
또한 객체에 Dates, functions, undefined, Infinity, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, sparse Arrays, Typed Arrays 또는 기타 복합 유형이 포함되어 있으면 이 방법이 작동하지 않는다.
const deepCopy = (data) => {
return JSON.parse(JSON.stringify(data));
}
const obj = {
a: 1,
b: {
c: 2,
},
};
const newObj = deepCopy(obj);
console.log(obj); // {a: 1, b: { c : 2 }}
console.log(newObj); // {a: 1, b: { c : 2 }}
console.log(obj === newObj) // false
// 객체 안의 객체도 변하지 않음
obj.b.c = 100;
console.log(obj); // {a: 1, b: { c : 100 }}
console.log(newObj); // {a: 1, b: { c : 2 }}
3. 라이브러리 사용(lodash)
var obj2 = _.cloneDeep(obj, true);
속도를 고려한 추천 방법
객체 안의 객체가 없다면 -> Object.assign()
deepClone이 필요한 경우 -> lodash 라이브러리 사용하거나 직접 함수 구현하기
reference
[Javascript] 얕은 복사, 깊은 복사
자바스크립트에서 값은 원시값과 참조값으로 나뉜다. 원시값 Number String Boolean Null Undefined 참조값 Object Symbol 원시값은 값을 복사 할 때 복사된 값을 다른 메모리에 할당 하기 때문에 원래의 값과
velog.io
'개발 > JavaScript' 카테고리의 다른 글
[Javascript] falsy? (0) | 2022.07.20 |
---|---|
[Javascript] The Basic Vanilla JavaScript Project Setup - 기본 Vanilla JS 프로젝트 세팅 방법 (번역) (0) | 2022.06.09 |
[JavaScript] 클린코드 참고자료 - 2 (객체) (0) | 2022.04.25 |
[JavaScript] 클린코드 참고자료 - 1 (변수, 배열) (0) | 2022.04.25 |
[Javascript] plain DB 데이터로 Tree 쉽게 만들기 (0) | 2022.04.14 |