ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • JavaScript 객체와 불변성이란 ?
    Frontend/JavaScript 2022. 7. 21. 17:49

    기본형 데이터와 참조형 데이터

    javascript의 데이터 타입

     데이터 타입은 값의 종류를 말한다. 자바스크립트의 모든 값은 데이터 타입을 갖는다. 7개의 데이터 타입을 제공하며, 7개의 데이터 타입은 기본형(원시형) 타입, 객체 타입으로 분류할 수 있다.

     

    구분 데이터 타입 설명
    기본형(원시형) 타입 숫자(number) 타입 숫자. 정수와 실수 구분없이 하나의 숫자 타입만 존재
    문자열(string) 타입 문자열
    불리언(boolean) 타입 논리적 참(ture)과 거짓(false)
    undefined 타입 var 키워드로 선언된 변수에 암묵적으로 할당되는 값
    null 타입 값이 없다는 것을 의도적으로 명시할 때 사용하는 값
    심벌(symbol) 타입 ES6에서 추가된 7번째 타입
    객체 타입   객체, 함수, 배열 등

     

    기본(원시)형에는 Number, String, Boolean, null, undefined 가 있으며 ES6 에서는 Symbol 도 추가되었다.

    참조형은 대표적으로 객체(Object)가 있고 그 하위에 배열(Array), 함수(Function), 정규표현식(RegExp) 등이 있으며,

    ES6에서는 Map, Set, WeakMap, WeakSet 등도 추가되었다.

     

    두 타입의 가장 대표적인 차이로는
    기본형에는 바로 값을 그대로 할당한다는 것이고 참조형에는 값이 저장된 주소값을 할당(참조)한다는 것이다.

     

     

     

    불변 객체를 만드는 방법

    우선적으로 객체란 무엇인가?

    자바스크립트는 객체 기반의 프로그래밍 언어이며, 자바스크립트를 구성하는 거의 "모든 것"이 객체다. 원시 값을 제외한 나머지 값(함수, 배열. 정규 표현식 등)은 모두 객체다.

     

    원시타입은 단 하나의 값만 나타내지만 객체타입은 다양한 타입의 값(원시 값 또는 다른 객체)을 하나의 단위로 구성한 복합적인 자료구조다. 또한 원시 타입의 값 즉 원시값은 변경 불가능한 값이지만 객체 타입의 값, 즉 객체는 변경 가능한 값이다. 
    객체는 0개 이상의 프로퍼티로 구성된 집합이며, 프로퍼티는 키(key)와 값(value)으로 구성된다.

     

    자바스크립트에서 사용할 수 있는 모든 값은 프로퍼티 값이 될 수 있다. 자바스크립트의 함수는 일급 객체(검색해보자)이므로 값으로 취급할 수 있다. 

     

     

    그렇다면 불변(immutability)이란 뭘까? 단어에서 유추해볼 수 있다시피 '변하지 않는' 뜻이라고 생각하면 되겠다.

    그럼 '불변 객체'란? '변하지 않는 객체' 즉 이미 할당된 객체가 변하지 않는다는 뜻을 가지고 있다.

    자바스크립트에서 불변 객체를 만들 수 있는 방법은 기본적으로 2가지 인데 const Object.freeze()를 사용하는 것이다.

     

     

    const

    자바스크립트 키워드 중 하나인 const이다. ES6문법부터 let과 const를 지원한다.

    const 키워드는 변수를 상수로 선언할 수 있다, 일반적으로 상수로 선언된 변수는 값을 바꾸지 못하는 것으로 알려져 있다.

    그렇다면 상수로 선언한 객체는 불변 객체일까? 

    const test = {};
    test.name = "gildong";
    
    console.log(test);  // {"gildong"}

    ES6에서의 const는 할당된 값이 상수가 되는 것이 아닌 바인딩된 값이 상수가 되는, 즉 test변수가 상수가 되기 때문에 const 키워드로 선언된 test변수에는 객체 재할당은 불가능하지만 객체의 속성은 변경 가능하다.

    재할당이 불가능 한 이유는 변수와 값(객체) 사이의 바인딩 자체가 변경이 되기 때문에 상수인 test변수는 재할당이 불가능한 것이고

    객체의 속성이 변경가능 한 이유는 실제 객체가 변경은 되지만 ( {} -> name : "mingyo" ) 객체와 변수(test)사이의 바인딩은 변경이 되지 않기 때문에 객체의 속성은 변경가능한 것이다. 

    때문에 비록 재할당은 불가능하지만 객체의 속성을 변경함으로 인해 변수에 바인딩된 객체의 내용까지 변경이 되기 때문에 불변객체라고 하기는 힘들다.

     

     

    Object.freeze()

    자바스크립트에서 기본적으로 제공하는 메소드인 Object.freeze() 메소드이다. 공식 문서에서는 "객체를 동결하기 위한 메소드" 라고 적혀있다.

    그렇다면 이 메소드를 사용하면 불변 객체를 만들 수 있을까?먼저 이 메소드의 사용법부터 알아보면,

    let test = {
        name : 'kim'
    }
    
    Object.freeze(test);

    사용법은 간단하다. test 변수에 key value를 가진 객체를 바인딩 후 Object.freeze(test)를 사용해 바인딩된 변수를 동결 객체로 만들었다. 때문에 test 객체는 객체의 속성을 변경하는 시도는 불가능하다.

    test.name = 'Choi';
    console.log(test) // {name: 'kim'}

    위와 같이 객체의 속성을 변경하는 시도는 무시된다.

    그러나 Object.freeze()는 동결된 객체를 반환하지만 객체의 재할당은 가능하다. 

     

    test = {
        age : 15
    };
    console.log(test); // {age: 15}

    위와 같이 객체의 재할당은 가능하다. 때문에 Object.freeze()도 불변 객체라고 할 수는 없을 것 같다.

    그럼 결국 불변 객체는 어떻게 만들 수 있냐면..

    const와 Object.freeze()를 조합하여 만들 수 있다. (const의 재할당불가 + Object.freeze()의 객체속성 변경불가)

    그렇다면 아래 코드와 같이 사용하면 된다.

     

    const test = {
        'name' : 'Choi'
    };
    
    Object.freeze(test);

    먼저 const키워드로 바인딩 된 변수를 상수화 시킨 다음, Object.freeze()로 해당 변수를 동결 객체를 만들면

    객체의 재할당과 객체의 속성 둘 다 변경불가능한 불변 객체가 된다.

     

     

     

    앝은 복사와 깊은 복사

    얕은 복사는 객체의 참조값(주소 값)을 복사하고, 깊은 복사는 객체의 실제 값을 복사한다. 

     

    얕은 복사 Shllow Copy

    얕은 복사란 객체를 복사할 때 위의 예제처럼 원래값과 복사된 값이 같은 참조를 가리키고있는 것을 말한다. 객체안에 객체가 있을 경우 한개의 객체라도 원본 객체를 참조하고 있다면 이를 얕은 복사라고 한다.

    다음은 얕은 복사를 하는 방법이다.

    1. Object.assign()

    Object.assign은 첫번째 요소로 들어온 객체에 다음인자로 들어온 객체를 복사해준다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = Object.assign({}, obj);
    
    copiedObj.b.c = 3
    
    obj === copiedObj // false
    obj.b.c === copiedObj.b.c // true

    2. 전개연산자

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = {...obj}
    
    copiedObj.b.c = 3
    
    obj === copiedObj // false
    obj.b.c === copiedObj.b.c // true

     

    깊은 복사 Deep Copy

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

    1. 재귀함수를 이용한 복사

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    function copyObj(obj) {
      const result = {};
    
      for (let key in obj) {
        if (typeof obj[key] === 'object') {
          result[key] = copyObj(obj[key]);
        } else {
          result[key] = obj[key];
        }
      }
    
      return result;
    }
    
    const copiedObj = copyObj(obj);
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false 

    2. JSON.stringify()

    JSON.stringify()는 객체를 json 문자열로 변환하는데 이과정에서 원본 객체와의 참조가 모두 끊어진다. 객체를 json 문자열로 변환후 JSON.parse()를 이용해 다시 자바스크립트 객체로 만들어주면 깊은 복사가 된다.

    하지만 이방법은 사용하기는 쉽지만 다른 방법에비해 아주 느리다고 알려져있다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = JSON.parse(JSON.stringify(obj));
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false 

    3. 라이브러리 사용

    lodash 라이브러리를 사용하면 깊은 복사를 더 쉽게 할 수 있다.

    const obj = {
      a: 1,
      b: {
        c: 2,
      },
    };
    
    const copiedObj = _.cloneDeep(obj);
    
    copiedObj.b.c = 3
    
    obj.b.c === copiedObj.b.c //false
Designed by Tistory.