자바스크립트 깊은복사, 얕은복사, object.assign()

제이밍·2021년 9월 22일
2

객체에서 깊은 복사와 얕은 복사란?

깊은복사 deep copy

  1. 객체에 중첩되어있는 객체까지 모두 복사
  2. 원시 값처럼 완전한 복사본을 만드는 것
  3. 원시값을 할당한 경우 깊은복사라 부르기도 함
// lodash의 cloneDeep을 사용한 깊은 복사
const o = {x: {y:1}};
const {cloneDeep} from 'lodash';

const c2 = cloneDeep(o)
console.log(c1 === o) //false
console.log(c1.x === o.x) // false
const v = 1;

const c1 = v;
console.log(c1 === v)

obj1과 obj2는 다른 주소를 가지게 된다.

실제로 웹 개발을 하다보면 lodash 모듈은 흔히 사용되며, 가장 손쉽게 객체의 깊은 복사를 해결하는 방법이라 할 수 있다.

얕은복사 shallow copy

  1. 한 단계 까지만 복사하는 것을 말한다.
  2. 객체에 중첩되어 있는 객체의 경우 참조 값을 복사한다.
  3. 객체를 할당한 변수를 다른 변수에 할당하는 것이라 부르기도 함.
const o = {x: {y:1}};

const c1 = {...o};
console.log(c1 === o) //false
console.log(c1.x === o.x) // true
const o = {x:1}

const c1 = o;
console.log(c1 === v) //true

객체를 직접 대입하는 경우 참조에 의한 할당이 이루어지므로 둘은 같은 데이터(주소)를 가지고 있다.

참조에 의한 객체 복사

객체원시타입의 근본적인 차이중 하나는 객체는 참조에 의해 저장되고 복사된다는 것이다.

원시값(문자열, 숫자, 불린 값)은 '값 그대로' 저장, 할당되고 복사되는 반면에 말이다.

let user = {
  name : "John"
  };

변수엔 객체 그대로 저장 되는 것이 아니라, 객체가 저장되어있는 메모리 주소인 객체의 참조값이 저장 된다. 👓

let user = { name: 'John' };
let user2 = { name: 'John' };
let admin = user;

admin.name = 'Pete'; 

console.log(user == user2) // false
console.log(user.name); // Pete

객체가 할당된 변수를 복사할 땐 객체의 참조 값이 복사되고 객체의 값이 복사되지 않는다.

참조에 의한 비교

let a = {};
let b = a; // 참조에 의한 복사

alert( a == b ); // true, 두 변수는 같은 객체를 참조합니다.
alert( a === b ); // true

두 변수는 같은 객체를 참조 하기때문에 동등연산자를 사용할 시 true라는 값을 반환합니다

let a = {};
let b = {}; // 독립된 두 객체

alert( a == b ); // false

반면, 독립된 두 객체에 대해서는 가지고 있는 주소값이 다르므로 false라는 값을 반환하게 된다.

참조값을 복사하지 않고 객체를 복사하려면 ?

1. for문으로 값을 하나하나 넣어 새로 만들어 주는 방법

let user = {
  name: "John",
  age: 30
};

let clone = {}; // 새로운 빈 객체

// 빈 객체에 user 프로퍼티 전부를 복사해 넣습니다.
for (let key in user) {
  clone[key] = user[key];
}

// 이제 clone은 완전히 독립적인 복제본이 되었습니다.
clone.name = "Pete"; // clone의 데이터를 변경합니다.

alert( user.name ); // 기존 객체에는 여전히 John이 있습니다.

2. object.assign을 활용하는 법

let user = {
  name: "John",
  age: 30
};

let clone = Object.assign({}. user);

여러 객체를 병합할 경우에도 사용 할 수 있다.

let user = { name: "John" };

let permissions1 = { canView: true };
let permissions2 = { canEdit: true };

// permissions1과 permissions2의 프로퍼티를 user로 복사합니다.
Object.assign(user, permissions1, permissions2);

// now user = { name: "John", canView: true, canEdit: true }

단 동일한 이름을 가진 프라퍼티가 있을 경우 오버라이딩되어 값이 덮어씌워짐
또한 이때 얕은 복사본은 중첩 객체를 처리하지 못한다

재미있는(FUN) 문제

var a = [1,2,3]
var b = [1,2,3]
var c = "1,2,3"

console.log(a==b) 
console.log(a==c)
console.log(a===b)

정답공개!

a와 b의 경우 원시값이 아닌 둘다 배열의 참조값을 가져와 비교함으로써 당연히! false

a==c의 경우 ===(Identity / strict equality) 가 아닌 ==(Equality) 를 사용했을때

Equality (==) (source)
The equality operator converts the operands if they are not of the same type,
then applies strict comparison. If both operands are objects, then JavaScript
compares internal references which are equal when operands refer to the same object in memory.

비교하는 두 대상이 다른 형태를 가지고 있을 경우 자바스크립트에서 두 형태를 같게 변형시킨후 비교하게 되어 a.toString === c 와 같이 비교되므로써 결과는 true가 나오게 됨

console.log(a==b) // false
console.log(a==c) // true
console.log(a===b)// false

Reference

https://javascript.info/object-copy
[JavaScript] 얕은 복사(Shallow Copy)와 깊은 복사(Deep Copy)

profile
모르는것은 그때그때 기록하기

0개의 댓글