데이터타입과 변경불가성

choi seung-i·2022년 3월 2일
0

JS로그

목록 보기
2/14
post-thumbnail

Immutability 참고

데이터 타입


simbol은 ECMAScript 6에서 새로 나옴




🔽 Primitive

  • 원시 데이터 타입

  • 더이상 쪼갤 수 없는 최소한의 데이터로 변경 불가능한 값(immutable value)
    변경이 불가능하다는 뜻은 메모리 영역에서의 변경이 불가능하다는 뜻으로, 재할당은 가능하다

    var str = 'Hello';
    str = 'world';
    //이떄는 Hello와 world는 각각 다른 방(메모리)에 들어가고, str은 world가 들어가있는 방(메모리)를 가르키도록 변경한 것

    • 일반 변수의 value가 동일하다면 같은 해당 값(value)이 있는 방을 가르켜서 비교하면 true가 됨
    • 객체의 경우 값(value)이 동일하더라도 각각 방으로 들어가기때문에 비교하면 false가 됨

🔽 Object

  • 객체는 참조형태로 전달하고 전달 받음

    var user = {
      name: 'Lee',
      address: {
        city: 'Seoul'
      }
    };
    
    var myName = user.name; // 변수 myName은 string 타입이다.
    
    user.name = 'Kim';
    console.log(myName); // Lee
    
    myName = user.name;  // 재할당
    console.log(myName); // Kim

    • 객체의 가변성 : 대입연산자(=)를 사용 할 경우 원본훼손을 함
      (만약 객체가 참조를 통해 공유되었다면 누군가가 원본을 변경하게되면 그 객체를 참조를 공유하는 모든 장소에서 영향을 받음)
  • 객체의 변경이 필요한 경우에는 참조가 아닌 객체의 방어적 복사(defensive copy) 를 통해 새로운 객체를 생성해서 사용해야 한다.
    => but, 객체가 변경 가능한 데이터를 많이 가지고 있다면 오히려 부적절 할 수도있음




불변 데이터 패턴 (Immutabillity data pattern)

Immutabillity(변경불가성)

  • 객체가 생성 된 이후 그 상태를 변경할 수 없는 디자인 패턴
  • 함수형 프로그래밍의 핵심 원리

🔽 Object.assign({target}, ...sources)

  • ES6에서 추가된 메소드

  • 대입연산자(=)로 그냥 넣어주는게 아닌 해당 메소드를 사용하여 복사해주는 것

  • target객체로 소스객체의 프로퍼티를 복사함
    target이 {}(빈객체)라면 소스객체만 복사해서 넣어 새 객체를 만듦

    // Copy
    const obj = { a: 1 };
    const copy = Object.assign({}, obj);
    console.log(copy); // { a: 1 }
    console.log(obj == copy); // false
    // Merge
    const o1 = { a: 1 };
    const o2 = { b: 2 };
    const o3 = { c: 3 };
    const merge1 = Object.assign(o1, o2, o3);
    console.log(merge1); // { a: 1, b: 2, c: 3 }
    console.log(o1);     // { a: 1, b: 2, c: 3 }, 타겟 객체(o1)가 변경된다!
    // Merge
    const o4 = { a: 1 };
    const o5 = { b: 2 };
    const o6 = { c: 3 };
    const merge2 = Object.assign({}, o4, o5, o6);
    console.log(merge2); // { a: 1, b: 2, c: 3 }
    console.log(o4);     // { a: 1 }, 타겟 객체({})에 소스만 넣어준다!

중첩된 객체의 복사

A객체 내에 B객체가 있을 경우 A를 assign으로 복제 해도 A객체는 각자 방을 잡지만 B객체는 동일한 방의 값을 가져다 씀
이 상태에서 o2.score.push(3) 해주면 o1의 score도 [1, 2, 3]으로 변질된다
이렇게 o2를 복제 후 o2.score.concat()으로 복사를 해줘야 B객체(내부 객체)도 따로 방을 잡음

  • Object.concat(추가할 값) : 배열 복사시 사용 / 추가 값이 없으면 그대로 복사

🔽 Object.freeze()

  • 객체를 불변하게 만들 수 있음

  • 한번 불변하게 만들면 풀 수 있는 방법은 없음. 원본을 복사해서 쓰는 방법뿐

    var o1 = {name:'kim', score:[1,2]}
    o1.name = 'lee';
    console.log(o1); // {name:'lee', score:[1,2]}
    
    /* freeze사용 : 바꿀 수 없음 */
    var o1 = {name:'kim', score:[1,2]}
    Object.freeze(o1);
    o1.name = 'lee';
    console.log(o1); /// {name:'kim', score:[1,2]}
    
    /* freeze를 사용해도 내부 객체는 변경 가능 (중첩된 객체 변경 가능) */
    var o1 = {name:'kim', score:[1,2]}
    Object.freeze(o1);
    o1.name = 'lee';
    o1.score.push(3);
    console.log(o1); /// {name:'kim', score:[1,2,3]}

  • 내부 객체까지 불변하게 만들려면 Deep freeze를 해야함

    function deepFreeze(obj) {
      const props = Object.getOwnPropertyNames(obj); //user객체의 모든 속성들을 배열로 반환하여 props에 넣어줌
    
      props.forEach((name) => {
        const prop = obj[name];  //props의obj[name]들을 prop에 넣어줌
        if(typeof prop === 'object' && prop !== null) {
          //만약 prop이 object이며 prop이 null이 아니라면 해당 prop을 deepFreeze를 해준다
          deepFreeze(prop); 
        }
      });
      return Object.freeze(obj); //user객체를 freeze시켜서 반환한다.
    }
    
    const user = {
      name: 'Lee',
      address: {
        city: 'Seoul'
      }
    };
    
    deepFreeze(user); //deepFreeze라는 이름의 함수로 user객체를 보내준다.
    
    user.name = 'Kim';           // 무시된다
    user.address.city = 'Busan'; // 무시된다
    
    console.log(user); // { name: 'Lee', address: { city: 'Seoul' } }

    Object.getOwnPropertyNames()


🔽 Immutable.js

  • Object.assign과 Object.freeze을 사용하여 불변 객체를 만드는 방법은 번거러울 뿐더러 성능상 이슈가 있어서 큰 객체에는 사용하지 않는 것이 좋다.

  • Facebook이 제공하는 방법

    • npm을 사용하여 Immutable.js를 설치
      $ npm install immutable
    //	Immutable.js의 Map 모듈을 임포트하여 사용
    const { Map } = require('immutable')
    const map1 = Map({ a: 1, b: 2, c: 3 })
    const map2 = map1.set('b', 50)
    map1.get('b') // 2  , map1.set(‘b’, 50)의 실행에도 불구하고 map1은 불변하였다.
    map2.get('b') // 50  , map1.set()은 결과를 반영한 새로운 객체를 반환한다.


공부하며 정리&기록하는 ._. 씅로그

profile
Front-end

0개의 댓글