얕은 복사 VS 깊은 복사

ChungsikPark·2021년 7월 13일
0

얕은 복사 (Shallow Clone)

  • 일반 참조

const original = {
  string: "hello",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean: true,
  function: function () {
    return "hello";
  },
};

const copied = original

original.c.d = 3000
console.log(copied.c.d) // 3000

이러한 형태의 참조 복사를 하게 되면, 동일한 객체의 주소를 바라보는 변수를 하나 더 만드는 것에 지나지 않는다. 이렇게 되면 하나의 객체가 두개의 변수에 의해서 공유되는 것이다. 이 경우에는 하나의 변수를 통해서 값을 변경했을 때, 나머지 변수에도 영향을 준다. 그러므로 최대한 지양하는 것이 좋다.

  • 전개 연산자 (Spread Operator) "..."

const original = {
  string: "hello",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean= true,
  function: function () {
    return "hello";
  },
};

const copied = { ...original }

original.string = "bye"
original.boolean = false
original.object.c.d = 3000
copied.array.push("D")

console.log(copied.string) // "hello"
console.log(copied.boolean) // true
console.log(copied.object.c.d) // 3000
console.log(original.array) // ["A", "B", "C", "D"]

최상위 레벨의 속성만을 봤을 땐, 원본과 복사본이 서로 영향을 받지 않는 것처럼 보인다. 하지만 불린 형태와 단순 속성이 아닌 객체나 배열에선 여전히 영향을 주고 있다.

  • 객체 메소드 Object.assign()

const original = {
  string: "hello",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean: true,
  function: function () {
    return "hello";
  },
};

const copied = Object.assign({}, original)

original.number = 456
original.boolean = false
copied.array.push("D")

console.log(copied.number) = 123
console.log(copied.boolean) // true
console.log(original.array) // ["A", "B", "C", "D"]

Object.assign()를 이용한 복사 또한 전개 연산자를 사용하여 복사한 경우와 결과가 같다.

깊은 복사 (Deep Clone)

  • JSON.stringify()

const original = {
  string: "hello",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean: true,
  function: function () {
    return "hello";
  },
};

const copied = JSON.parse(JSON.stringify(original));

original.string = "bye"
original.boolean = false
original.object.c.d = 3000

console.log(copied.string) // "hello"
console.log(copied.boolean) // true
console.log(copied.object.c.d) // 3
console.log(copied.function) // undefined

JSON.stringify()은 자바스크립트 객체를 JSON 파일로 바꾸고, JSON.parse()는 JSON 파일을 다시 자바스크립트 객체 형태로 바꾸어 주는 것을 말한다. 이 과정에서 객체에 대한 참조가 사라지면서, 원본과 복사본을 다르게 다룰 수 있게 된다. 하지만 이 방법은 엇밀히 말해서 완벽한 방법을 아니다. 왜냐하면 function의 경우 복사본에 undefined로 출력하기 때문이다.

  • _.cloneDeep(obj)

const original = {
  string: "hello",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean: true,
  function: function () {
    return "hello";
  },
};

const copied = _.cloneDeep(original);

original.string = "bye"
original.boolean = false
original.object.c.d = 3000

console.log(copied.string) // "hello"
console.log(copied.boolean) // true
console.log(copied.object.c.d) // 3

Lodash는 자바스크립트 라이브러리 중의 하나이다. 물론 사용하기 위해서는 설정이 필요하다.

// In a browser

<script src="lodash.js"></script>

// Using npm

$ npm i -g npm
$ npm i --save lodash

Lodash 메서드 중에서 cloneDeep 메서드를 사용하면 깊은 복사가 가능하다.

  • 재귀 함수를 이용하여 직접 구현

const clone = (input) => {
  const output = {};
  for (let i in input) {
    if (input[i] !== null && typeof input[i] === "object") {
      output[i] = clone(input[i]);
    } else {
      output[i] = input[i];
    }
  }
  return output;
}

const original = {
  string: "abc",
  number: 123,
  object: {
    a: 1,
    b: 2,
    c: {
      d: 3,
    },
  },
  array: ["A", "B", "C"],
  boolean: true,
  function: function () {
    return "abc";
  },
};

const copied = clone(original)

original.function = function () {
  return "def";
};

console.log(copied.function) // function () { return "abc"; }

재귀함수를 이용해서 직접 구현도 가능하다.

profile
Blog by Chungsik Park

0개의 댓글