본문 바로가기

WEB/HTML CSS JS

[JS] 얕은 복사 깊은 복사

코어 자바스크립트 1장을 읽고 정리한 내용 (2)

4. 얕은 복사와 깊은 복사   
    4-1. 불변 객체
    4-2. 복사한 데이터를 변경할 때 원본 데이터의 변경 여부   
    4-3. 불변객체를 만드는 방법

 

1. 불변 객체 (immutable object)

리액트 토이프로젝트를 하면서 객체의 불변성을 신경쓰지 않다가 문제가 발생한 적이 없어서 아직 중요성을 못 느끼고 있지만 (?)

불변 객체는 React, Vue.js, Angular 등의 라이브러리나 프레임워크 뿐만 아니라 함수형 프로그래밍, 디자인 패턴 등에서 매우 중요한 기초가 되는 개념이라고 한다. 불변 객체의 개념이나 등장배경, 필요성에 대한 설명이 위키피디아에 잘 정리되어 있다.

 

 

불변객체 - 위키백과, 우리 모두의 백과사전

위키백과, 우리 모두의 백과사전. 객체 지향 프로그래밍에 있어서 불변객체(immutable object)는 생성 후 그 상태를 바꿀 수 없는 객체를 말한다. 반대 개념으로는 가변(mutable) 객체로 생성 후에도 상

ko.wikipedia.org

아무튼 Flux 패턴을 쓰는 Redux를 쓰는 React 가 대세가 되면서 Javascript 에서 불변 객체를 다루는게 새로운 트렌드라고 함.

리액트에서의 불변성에 대해서는 포스팅을 따로 해봐야겠다.

 

불변 객체가 필요한 상황

값으로 전달받은 객체에 변경을 가하더라도, 원본 객체는 변하지 않아야 하는 경우가 종종 있다. 이런 경우에 불변 객체가 필요하다.

 


 

2. 복사한 데이터를 변경할 때 원본 데이터까지 변경되는 문제

 

불변값인 primitive type 데이터들은 그냥 써도 문제없지만, 가변값인 reference type 데이터들은 복사하고 값을 바꾸면 원본도 바뀔텐데 어쩌지?

 

예를 들어, 이런 문제가 있다. (객체의 가변성에 따른 문제점)

아래 예시에서 user 와 user2 는 같은 객체를 가리키고 있기 때문에, user2.name 을 변경했는데 user.name 까지 바뀌어버렸다.

var user = {
    name: 'Amy'
    gender: 'female'
};

var changeName = function (user, newName) {
    var newUser = user;
    newUser.name = newName;
    return newUser;
};

var user2 = changeName(user, 'Bob');

console.log(user.name, user2.name);		// Bob Bob
console.log(user === user2);			// true

 

이렇게 다른 객체를 가리키게 하는 방법을 쓰면 위 문제는 해결이 되지만, 이 방법은 변경하지 않고 복사만 할 프로퍼티에 대해 하드코딩을 하는 수밖에 없어서 비효율적이다.

var user = {
    name: 'Amy'
    gender: 'female'
};

var changeName = function (user, newName) {
    return {
        name: newName,
        gender: user.gender
    };
};

var user2 = changeName(user, 'Bob');

console.log(user.name, user2.name);		// Amy Bob
console.log(user === user2);			// false

 

역시 객체의 모든 프로퍼티를 for 문으로 돌면서 복사하는 함수를 만들어야될 것 같다!

 


 

3. 불변 객체를 만드는 방법 

얕은 복사

var copyObject = function (target) {
    var result = {};
    for (var prop in target) {
        result[prop] = target[prop];
    }
    return result;
}

 

 

아래와 같은 중첩 객체에 대해서는 얕은 복사를 하면 문제가 있다.

urls 의 주솟값만 복사되기 때문에, 복사본을 바꾸면 원본도 바뀌는 문제가 또다시 발생하는 것이다.

var user = {
    name: 'hjung',
    urls: {
        portfolio: 'https://github.com/hysimok',
        blog: 'http://blog.com',
        facebook: 'http://facebook.com/abc'
    }
};

깊은 복사

var copyObjectDeep = function(target) {
    var result = {};
    if (typeof target === 'object' && target !== null) {
        for (var prop in target) {
            result[prop] = copyObjectDeep(target[prop]);
        }
    } else {
        result = target;
    }
    return result;
};

 

깊은 복사를 간단하게 할 수 있는 방법

JSON 메서드 활용

객체를 JSON 문법으로 표현된 문자열로 전환했다가 다시 JSON 객체로 바꾼다.

var copyObjectViaJSON = function (target) {
    return JSON.parse(JSON.stringify(target));
};