[Javascript] 얕은 복사와 깊은 복사

채린·2023년 3월 21일
1

Javascript

목록 보기
1/4
post-thumbnail

👀 mutable & immutable

  • mutable : 변경 가능한 (즉, 원본이 변경 가능)
  • immutable : 변경 가능하지 않은 (즉, 원본이 변경 가능하지 않은) - 데이터 원본이 훼손되는 걸 막을 수 있음

❔ Javascript의 데이터 타입 (원시값과 객체)

(1) Primitive: 원시 데이터 타입 (string, number, bigint, boolean, undefined, symbol, null) => 모든 원시값은 불변하여 변형할 수 없음 (immutable)

원시값을 교체할 수는 있지만, 직접 변형할 수는 없음

(2) Object: 객체 (Object, Array, Function)
-> 할당된 변수를 조작하는 것은 사실 객체 자체를 조작하는 것이 아닌, 해당 객체의 참조를 조작하는 것임
(mutable)

  • 원시 데이터 타입 예시
// 문자열 메서드는 문자열을 변형하지 않음
let bar = "baz";
console.log(bar);        // baz
bar.toUpperCase();
console.log(bar);        // baz



// 할당은 원시 값에 새로운 값을 부여 (변형이 아님)
bar = bar.toUpperCase(); // BAZ 
let a = 1
let b = 1 
console.log(a, b, a===b) //  1 1 true 

  • 객체 타입 예시
// 배열 메소드는 배열을 변형함
let foo = [];
console.log(foo);        // []
foo.push("plugh");
console.log(foo);        // ["plugh"] 
let c = {name: 'chaelin'}
let d = {name: 'chaelin'} 
console.log(c, d, c===d) 
# { name: 'chaelin' } { name: 'chaelin' } false 
> 원시 데이터 타입은 값이 같으면 같은 주소를 가리키지만 
객체는 그렇지 않음

let a = {name: 'chaelin'}
let b = {name: 'chaelin'}
let c = a  

c.name = 'chae'

👀 얕은 복사 (Shallow Copy)

  • 참조값(주소값)을 복사

    얕은 복사란 객체를 복사할 때 기존 값과 복사된 값이 같은 참조를 가리키고 있는 것을 말함 (객체 안에 객체가 있을 경우 한 개의 객체라도 기존 변수의 객체를 참조하고 있다면 이를 얕은 복사라고 함)

Array.prototype.slice()

const original = ['a',2,true,4,"hi"];
const copy = original.slice();
 
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
 
copy.push(10);
 
console.log(JSON.stringify(original) === JSON.stringify(copy)); // false
 
console.log(original); // [ 'a', 2, true, 4, 'hi' ]
console.log(copy); // [ 'a', 2, true, 4, 'hi', 10 ] 

얕은 복사, 배열안에 들어있는 원시값은 기본적으로 깊은 복사임

const original = [
  [1, 1, 1, 1],
  [0, 0, 0, 0],
  [2, 2, 2, 2],
  [3, 3, 3, 3],
];
 
const copy = original.slice();
 
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
 
// 복사된 배열에만 변경과 추가.
copy[0][0] = 99; 
copy[2].push(98);
 
console.log(JSON.stringify(original) === JSON.stringify(copy)); // true
 
console.log(original);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력
console.log(copy);
// [ [ 99, 1, 1, 1 ], [ 0, 0, 0, 0 ], [ 2, 2, 2, 2, 98 ], [ 3, 3, 3, 3 ] ]출력 

배열안에 배열은 얕은 복사가 된 것임

Object.assign

  • 첫 번째 인자로 빈 객체를 넣어주고 두 번째 인자로 복사할 객체를 넣어주면 됨
let a = {name: 'chaelin'} 
let b = Object.assign({}, a)
b.name = 'chae'
console.log(a, b, a===b) 
# { name: 'chaelin' } { name: 'chae' } false 

let a = {name: 'chaelin', score: [10, 20] } 
let b = Object.assign({}, a) 

let a = {name: 'chaelin', score: [10, 20] } 
let b = Object.assign({}, a)  
b.score = b.score.concat() 
  • concat 복제하다는 뜻
let a = {name: 'chaelin', score: [10, 20] } 
let b = Object.assign({}, a)  
b.score = b.score.concat() 
b.score.push(3)
console.log(a === b, a.score === b.score)  
# false false 

Spread 연산자 (...)

const fruitOne = ['apple', 'banana'];
const fruitTwo = ['grape', 'peach'];

// ES6 spread 연산사 활용 방법
const fruitAll = [...fruitOne, ...fruitTwo];

console.log(fruitAll); // ['apple', 'banana', 'grape', 'peach'] 

👀 깊은 복사 (Deep copy)

  • 객체의 실제 값을 복사

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

JSON.parse & JSON.stringify

  • 객체를 json 문자열로 변환하는데 이 과정에서 원본 객체와의 참조가 모두 끊어짐
  • 객체를 json 문자열로 변환 -> JSON.parse() 를 이용해 다시 원래 객체로 만들어줌
  • 느리다는 단점이 있음
const object = {
  a: "a",
  number: {
    one: 1,
    two: 2,
  },
  arr: [1, 2, [3, 4]],
};
 
const copy = JSON.parse(JSON.stringify(object));
 
copy.number.one = 3;
copy.arr[2].push(5);
 
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
 
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }

재귀 함수를 구현한 복사

  • 복잡함
const object = {
  a: "a",
  number: {
    one: 1,
    two: 2,
  },
  arr: [1, 2, [3, 4]],
};
 
function deepCopy(object) {
  if (object === null || typeof object !== "object") {
    return object;
  }
  // 객체인지 배열인지 판단
  const copy = Array.isArray(object) ? [] : {};
 
  for (let key of Object.keys(object)) {
    copy[key] = deepCopy(object[key]);
  }
 
  return copy;
}
 
const copy = deepCopy(object);
 
copy.number.one = 3;
copy.arr[2].push(5);
 
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
 
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }

Lodash 라이브러리 사용

  • 라이브러리를 사용하면 더 쉽고 안전하게 깊은 복사를 할 수 있음 (설치 필요)
const deepCopy = require("lodash.clonedeep")
 
const object = {
  a: "a",
  number: {
    one: 1,
    two: 2,
  },
  arr: [1, 2, [3, 4]],
};
 
const copy = deepCopy(object);
 
copy.number.one = 3;
copy.arr[2].push(5);
 
console.log(object === copy); // false
console.log(object.number.one === copy.number.one); // false
console.log(object.arr === copy.arr); // false
 
console.log(object); // { a: 'a', number: { one: 1, two: 2 }, arr: [ 1, 2, [ 3, 4 ] ] }
console.log(copy); // { a: 'a', number: { one: 3, two: 2 }, arr: [ 1, 2, [ 3, 4, 5 ] ] }

📚 참고

https://developer.mozilla.org/ko/docs/Glossary/Primitive
https://pythontutor.com/
https://bbangson.tistory.com/78

Image by Ylanite Koppens from Pixabay

0개의 댓글