Javascript 객체 복제 방법

개발새발개발러·2023년 1월 17일
0

Javascript

목록 보기
3/3
post-thumbnail
const original = {
  num: 1000,
  bool: true,
  str: "test",
  func: function () {
    console.log("func");
  },
  obj: {
    x: 1,
    y: 2,
  },
  arr: ["A", "B", "C"],
};

자바스크립트에서 객체는 key와 value를 가지는데 위의 예시처럼 key값은 문자열인 것과 비교하면 값은 모든 데이터형을 가질 수 있다는 특징이 있다.

1. 참조할당

객체를 복제할 때 초보자들이 가장 많이 하는 실수는 다음과 같이 =연산자를 통해 새로운 변수에 복사할 객체를 할당하는 것이다.

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elemsntSchool : "test3"
	}
}
const person2 = person1;// 참조할당
p1.age = 30;

console.log(p1.age)//30
console.log(p2.age)//30

person2에 person1을 할당할 때, person1의 값 전체가 복사되는 것이 아니라 주소값이 복사된 것이기 때문에 person1의 값이 변경이 되면 person2의 값도 함께 변경이 된다.

즉 person2라는 객체는 person1이라는 객체의 값을 복사 한 것이 아닌 person1객체의 값을 참조한다는 의미

2. 얕은 복제(Shallow Clone)

객체를 복제할 때 다음으로 많이 하는 방법은 객체의 얕은 복사

2-1 Object.assign()

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elementSchool : "test3"
	}
};

const target = {
	position = "Frontend"
};

const person2 = Object.assign(target, person1);

person2.age = 30;

console.log(person2.age) //30
console.log(target.age) //30
console.log(person1.age) //32

//person2의 age를 변경하더라도 person1의 age는 변경되지 않는다.

Object.assign(target, …sources) 메서드를 사용하면 첫번째 인자로 두번째 인자의 속성들을 복사할 수 있다. 따라서 위와같이 언뜻 보면 원본과 복사본이 서로 영향을 주지 않고 변경이 가능한 것처럼 보인다.

하지만 단순 속성이 아닌 객체나 배열 속성을 변경해보면 아래와 같이 여전히 서로 영향을 준다는 것을 알 수 있다.

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elementSchool : "test3"
	}
};

const target = {
	position = "Frontend"
};

const person2 = Object.assign(target, person1);

person2.school.highSchool = "seoulHighSchool";

console.log(person2.school.highSchool) //seoulHighSchool
console.log(person1.school.highSchool) //seoulHighSchool

이런 현상이 발생하는 이유는 객체를 하나의 트리구조로 봤을 때 최상위 레벨의 속성만 복사를 하는 Object.assign(target, …person1)메서드의 동작방식에 있다. 객체트리의 최말단 노드까지 복사되지 않기 때문에 이러한 복제방식을 얕은복제(shallow clone)라고 일컫는다.

2-2 …(Spread Operator)

물론 의도적으로 깊은 복제 대신에 얕은 복제를 하는 경우도 있을 수 있다. 얕은복제는 아래와 같이 …(Spread Operator)를 활용해서 더 간결하게 처리 할 수 있다.

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elementSchool : "test3"
	}
};
const person2 = {...person1}

person2.name = "Kim"
person2.school.highSchool = "seoulHighSchool";

console.log(person1.name)//kim
console.log(person2.name)//dong
console.log(person2.school.highSchool) //seoulHighSchool
console.log(person1.school.highSchool) //seoulHighSchool
  • Object.assign() 과 동일하게 얕은 복사의 문제점을 가지고 있다.

3. 깊은 복제(Deep Clone)

객체 트리의 최말단 노드까지 복제하고 싶을때는 어떻게 처리해야될까?

3-1 JSON.parse(JSON.stringify(object))

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elementSchool : "test3"
	},
	test: () => {
		console.log("function test")
	}
};

const person2 = JSON.parse(JSON.stringify(person1));

person2.age = 30;
person2.school.highSchool = "seoulHighSchool"

console.log(person1.age) //32
console.log(person2.age) //30

//객체트리의 최말단 영역까지 copy가능
console.log(person1.school.highSchool) //test
console.log(person2.school.highSchool) //seoulHighSchool

위와 같이 JSON객체의 메소드를 이용하여 deep copy를 할 수 있다.

→ JSON.stringify로 객체트리의 최말단영역까지 문자열로 변환 후 JSON.parse로 자바스크립트 객체로 변환

JSON문자열로 변환하고 다시 객체로 변환해 주기 때문에 객체에 대한 참조가 없어진다.

  • JSON.parse(JSON.stringify(object)) 문제점
    • 다른 방법에 비해 성능적으로 느리다.
    • JSON.stringify 메소드가 객체안의 function, Date객체, 정규표현식 등등의 데이터는 복사되지 않고 유실된다.

3-2 커스텀 재귀함수 사용

deep clone 방법으로는 직접 깊은 복사(deep clone)를 실현하는 함수를 짜는것이다.

복사를 진행하다가 객체를 만나면 함수를 재귀적으로 실행해 깊은 복사(deep clone)를 실현 하는 것인데

function cloneObject(obj) {
  var clone = {};
  for (var key in obj) {
    if (typeof obj[key] == "object" && obj[key] != null) {
      clone[key] = cloneObject(obj[key]);
    } else {
      clone[key] = obj[key];
    }
  }

  return clone;
}

객체의 프로퍼티를 순환하며 빈 오브젝트에 더하고, 프로퍼티가 오브젝트일 경우 재귀적으로 함수를 실행하며 복사를 진행한다.

3-3 lodash의 cloneDeep()사용

마지막으로 가장 손쉽게 Lodash를 사용해 깊은복사하는 방법이 있다 이미 코드내에서 기존에 Lodash를 사용하고 있었다면 Lodash를 활용해 깊은 복사를 진행하는것도 좋은 선택지이다.

const person1 = {
	name:"dong",
	age : 32,
	school : {
		highSchool : "test",
		middleSchool : "test2",
		elementSchool : "test3"
	},
	test: () => {
		console.log("function test")
	}
};
let person2 = _.cloneDeep(person1);

person2.school.highSchool = "seoulHighSchool";
console.log(person1.school.highSchool); //test
console.log(person2.school.highSchool); //seoulHighSchool

0개의 댓글