맞습니다. 객체는 가변성 데이터 타입입니다.
객체를 복사해, 새로운 객체로 만든 뒤 값을 변경하면
let obj = {a:1, b:1, c:2};
obj1 = obj;
obj1.a = 30;
console.log(obj1.a, obj.a) // 30, 30
이런식으로, 기존의 obj 의 a 값도 같이 변경되는 모습을 확인할 수 있습니다.
이러한 현상을 방지하기 위해, 복사를 제대로 한 뒤 새로운 객체를 만들어야합니다.
(기존 데이터를 건들지 않고, 새롭게 복사한 객체의 값만 수정되도록 함)
얕은 복사란?
바로 아래 단계의 값만 복사함.
(복사본이 원본의 속성과 동일한 참조(메모리주소)를 공유하는 복사)
let copyObject = function(target){
let result = {};
for (let prop in target){
result[prop] = target[prop];
}
return result;
}
for in 문법을 사용해 새로운 result 객체에 target 객체의 프로퍼티를 복사하는 함수
var user = {
name: "yujin",
gender: "male",
};
var user2 = copyObject(user);
user2.name = "jin";
if (user !== user2) {
console.log("유저 정보가 변경되었습니다."); // 유저 정보가 변경되었습니다.
}
console.log(user.name, user2.name); // yujin jin
console.log(user === user2); // false
이러한 방식을 사용하면
프로토타입 체이닝 상의 모든 프로퍼티를 복사하는 점과,
getter/setter는 복사되지 않는 점,
얕은 복사만을 수행하는다는 점의 단점이 있습니다.
let copyObject = function(target){
let result = {};
for (let prop in target){
result[prop] = target[prop];
}
return result;
}
let user ={
name: "yujin",
urls: {
port : "http://portfolio",
blog: "http://blog",
instar: "http://instar"
}
}
let user2 = copyObject(user);
user2.name = "jin";
console.log(user.name === user2.name) // false
user.urls.port = "http://port";
console.log(user.urls.port === user2.urls.port) // true
얕은 복사를 통해, 객체에 속한 프로퍼티도 복사가 되지만,
기존 데이터를 참조하고 있는 모습을 확인할 수 있습니다.
참조를 하고 있다면 변경이 된다면 참조하고 있는 복사 객체에게도 값이 영향이 간다는 뜻입니다.
이러한 현상이 발생하지 않게 하려면 내부 프로퍼티에 대해서도 불변 객체로 만들어야합니다.
-> 깊은 복사를 한다.
let copyObject = function(target){
let result = {};
for (let prop in target){
result[prop] = target[prop];
}
return result;
}
let user ={
name: "yujin",
urls: {
port : "http://portfolio",
blog: "http://blog",
instar: "http://instar"
}
}
let user2 = copyObject(user);
user2.urls = copyObject(user.urls);
user.urls.port = "http://port1";
console.log(user.urls.port === user2.urls.port) // false
객체 내부의 프로퍼티까지 함수를 사용해 복사를 하여 새로운 데이터로 만든 결과, 내부 프로퍼티까지 참조가 되지 않는 복사가 잘 된 모습을 확인할 수 있습니다.
하지만 참조형 데이터가 있을 때마다 재귀적으로 수행해야만 깊은 복사가 된다는 점이 참 불편합니다.
const copyObject = (target) => {
return JSON.parse(JSON.stringify(target))
}
let obj = {
a: 1,
b: {
c: null,
d: [1,2],
func1 : () => {console.log(3)}
}
}
let obj2 = copyObject(obj);
obj2.a = 3;
obj2.b.c = 4;
console.log(obj.a, obj2.a) // 1, 3
console.log(obj.b.c, obj2.b.c) // null, 4
console.log(obj.b.func1, obj2.b.func1) // () => {console.log(3)}, undefined
JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾸는 방법입니다.
하지만 메서드(함수)나 숨겨진 프로퍼티는 모두 무시합니다.
그래서 http 요청으로 받은 데이터를 저장한 객체를 복사할 때 등 순수한 정보만 다룰 때 사용하기 좋은 방법입니다.
Lodash의 cloneDeep 사용
var objects = [{ 'a': 1 }, { 'b': 2 }];
var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
객체나, 배열 안에 불변값만 있는 경우에는 얕은 복사만 해도 괜찮다.
let arr = [1,2,3] let obj = {a: 1, b:"string"} let arr1 = [...arr] let obj1 = {...obj}