티스토리 뷰

복사에 대해서 이해하려면 우선 자바스크립트의 데이터타입에 대한 이해가 필요하다. 

복사는 어떤 데이터 타입인지에 따라 다르게 진행되기 때문인데  자바스크립트에서 데이터는 원시타입과 참조타입으로 나뉜다. 

변수에 원시 값을 담는다면 값 자체가 담긴다. 하지만 변수에 객체를 담는다면 객체 값이 아니라 객체 값이 저장된 메모리 주소가 담긴다.

자세한 내용은 아래의 내용을 참고하면 된다. 

 

원시값: number/string/boolean/null/undefined/ symbol(ES6) -> 변경 불가능한 값 (immutable value)

참조값: Object(array, function, regExp)/ Map,Set 등(ES6) ->  변경 가능한 값(mutable value), 원시 값을 제외한 모든 것

// 문자열 메서드는 문자열을 변형하지 않음
var bar = "baz";
console.log(bar);        // baz
bar.toUpperCase();
console.log(bar);        // baz

// 배열 메소드는 배열을 변형함
var foo = [];
console.log(foo);        // []
foo.push("plugh");
console.log(foo);        // ["plugh"]

// 할당은 원시 값에 새로운 값을 부여 (변형이 아님)
bar = bar.toUpperCase(); // BAZ

원시 값을 교체할 수는 있지만, 직접 변형할 수는 없습니다.

 

=> 원시 값은 값을 복사할 때 복사된 값을 다른 메모리에 할당하기 때문에 원래의 값과 복사된 값이 서로에게 영향을 미치지 않는다. 

반면, 참조값은 변수가 객체의 주소를 가리키는 값이기 때문에 복사된 값(주소)이 같은 값을 가리킨다.

 

 

객체의 프로퍼티는 데이터 형태가 어떤 것이냐에 따라서 또 원시값과 참조값이 갈리므로

a.name 과 b.name은 객체타입을 비교하는거겠네? 가 아니라 name이라는 원시타입의 변수를 비교하는 것으로 

const a = { 
 	name: 'yj',
  	age: 9
};
const b = { 
 	name: 'yj',
  	age: 9
};

console.log(a === b); // false: 각각 객체를 할당했기 때문에 참조 값 다름
console.log(a.name === b.name); // true: 값 자체 비교

원시 타입인 변수끼리 비교한다면, 값 자체를 비교하고, 

객체 타입인 변수끼리 비교한다면, 참조 값을 비교한다. 

 

const a = { age: 12};
let b = a;
b.age = 2; 
console.log(a,b); //{age: 2}, {age: 2}

이러한 객체의 특성 때문에 객체를 복사하는 방법은 크게 두 가지로 나뉜다. 

🤸‍♀️깊은 복사(1depth 까지만, 그 이후는 안됨)

[개념]

얕은 복사란 객체를 복사할 때 위의 예제처럼 원래 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말한다.

객체 안에 객체가 있을 경우, 한 개의 객체라도 원본 객체를 참조하고 있다면 이를 '얕은 복사'라고 한다.

*2022 10 04 assign과 전개 연산자는 1depth까지만 깊은 복사가 되고 그 안으로 depth가 깊어지면 깊은 복사가 되지 않기 때문에 얕은 복사라고 칭하였다. 하지만, 이는 보는 관점에 따라 다르기 때문에 얕은 복사의 경우는 개념만 알아두면 좋을 거 같다.

 

[깊은 복사를 하는 법]

  • Object.assign() - 첫번째 요소로 들어온 객체에 다음 인자로 들어온 객체를 복사해준다. 
const obj = {
a:1,
b: {
	c: 2,
	},
};

const copiedObj = Object.assign({}, obj);

copidObj.b.c = 3;
obj === copiedObj // false
obj.b.c === copiedObj.b.c // true
  • 전개 연산자 (spread operator)
const obj = {
a:1,
b: {
	c: 2,
	},
};

const copiedObj = {...obj};

copidObj.b.c = 3;
obj === copiedObj // false
obj.b.c === copiedObj.b.c // true

🤸‍♀️깊은 복사 (Deep Copy)

[개념]

깊은 복사된 객체는 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말한다.

 

[깊은 복사를 하는법]

  • 재귀 함수를 이용한 복사 -> 빈 객체를 새롭게 만들고 각각의 key value를 넣어주는 느낌
const obj = {
  a: 1,
  b: {
    c: 2,
  },
};

function copyObj(obj) {
  const result = {};

  for (let key in obj) {
    if (typeof obj[key] === 'object') {
      result[key] = copyObj(obj[key]);
    } else {
      result[key] = obj[key];
    }
  }

  return result;
}

const copiedObj = copyObj(obj);

copiedObj.b.c = 3

obj.b.c === copiedObj.b.c //false
  • JSON.stringify() - 해당 메소드는 객체를 json 문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어진다. 객체를 json문자열로 변환 후 JSON.parse()를 이용하여 다시 자바스크립트 객체로 만들어주면 깊은 복사가 된다. 
    • 해당 방법은 사용하기 쉽지만 다른 방법에 비해 아주 느리다고 알려져있다. 
const copiedObj = JSON.parse(JSON.stringify(obj));

copiedObj.b.c = 3;
obj.b.c === copiedObj.b.c //false
  • lodash 라이브러리 사용하기
const copiedObj = _.cloneDeep(obj);
copiedObj.b.c =3;
obj.b.c === copiedObj.b.c // false

 

한 문장 정리 

얕은 복사란 객체를 복사했을 때 원래 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말합니다. 객체 안에 객체가 있을 경우, 한 개의 객체라도 원본 객체를 참조하고 있다면 이를 "얕은 복사"라고 합니다. 깊은 복사란 객체를 복사했을 때, 객체 안에 객체가 있을 경우에도 원본과의 참조가 완전히 끊어진 객체를 말합니다.

복사는 참조형 데이터가 메모리 주소를 할당하기 때문에 2가지로 분류됩니다. 얕은 복사는 원본과 참조값을 공유하고 깊은 복사는 원본과의 참조를 끊어냅니다. 데이터 변형을 했을 때 원본에 영향을 미치고 싶지 않다면 깊은 복사를 하면 됩니다.

-깊은 복사 방법(1depth 한정): Object.assign() / spread operator(전개 연산자, ES6) 
-깊은 복사 방법: 재귀함수 사용 / lodash 라이브러리 활용 / JSON화 했다가 다시 parse하는 방법
댓글