Javascript Immutability
Immutability(변경불가성)이란? 객체가 생성된 이후 그 상태를 변경할 수 없는 디자인 패턴을 의미한다.
레퍼런스를 참조한 다른 객체에서 객체를 변경하는 상황을 해결하는 방법
객체를 불변객체로 만들어 프로퍼티의 변경을 방지하며 객체의 변경이 필요한 경우에는 참조가 아닌 객체의 방어적 복사(defensive copy)를 통해 새로운 객체를 생성한 후 변경한다. 또는 Observer 패턴으로 객체의 변경에 대처할 수도 있다.
Observer
패턴으로 객체의 변경에 대처할 수도 있다.
데이터 타입이 필요한이유 ? 메모리를 효율적으로 사용하기 위해서
1. immutable value vs. mutable value
Javascript의 기본 자료형(primitive data type)은 변경 불가능한 값(immutable value)이다.
- Boolean
- null
- undefined
- Number
- String
- Symbol (New in ECMAScript 6)
객체 타입은 변경 가능한 값(mutable value)이다. 즉
객체는 새로운 값을 다시 만들 필요없이 직접 변경이 가능하다는 것이다.
1 | var statement = 'I am an immutable value'; // string은 immutable value |
- slice() 메서드는 statement 변수에 저장된 문자열을 변경하는 것이 아니라 사실은
새로운 문자열을 생성하여 반환하고 있다.(immutable value)
1 | var arr = []; |
- 처리 후 결과의 복사본을 리턴하는 문자열의 메서드 slice()와는 달리 배열(객체)의 메서드 push()는 직접 대상 배열을 변경한다. 그 이유는 배열은
객체이고 객체는 immutable value가 아닌 변경 가능한 값이기 때문이다.
1 | var user = { |
- user.name의 값을 변경했지만 변수 myName의 값은 변경되지 않았다.
이는 user.name의 타입 string이 변경 불가능하기 때문이다.
- myName은 기본형으로써 값을 pass-by-value(복사)하기 때문에 user.name=’kim’이 바로 반영되지 않는다.
1 | var user1 = { |
- var user2 = user1으로 인해서 user2가
객체 타입으로 변했기에 참조형으로 반영된다.
- 객체를 변경못하게 불변객체로 만들어야한다.
2. 불변 데이터 패턴(immutable data pattern)
- 의도하지 않은 객체의 변경을 막는 방법 : 객체를 불변객체로 만들어 프로퍼티의 변경을 방지하며 객체의
변경이 필요한 경우에는 참조가 아닌 객체의 방어적 복사(defensive copy)를 통해 새로운 객체를 생성한 후 변경한다.
2.1 Obejct.assign
Object.assign은 타킷 객체로 소스 객체의 프로퍼티를 복사한다.
소스 객체의 프로퍼티와 동일한 프로퍼티를 가진 타켓 객체의 프로퍼티들은 소스 객체의 프로퍼티로 덮어쓰기된다.
- 리턴값으로 타킷 객체를 반환한다.
copy의 역할을 한다.
1 | // Copy |
- Object.assign을 사용하여 기존 객체를 변경하지 않고 객체를 복사하여 사용할 수 있다.
1 | const user1 = { |
user1 객체를 빈객체에 복사하여 새로운 객체 user2를 생성하였다. user1과 user2는 어드레스를 공유하지 않으므로 한 객체를 변경하여도 다른 객체에
아무런 영향을 주지 않는다.
주의할 것은 user1 객체는 const로 선언되어 재할당은 할 수 없지만 객체의 프로퍼티는 보호되지 않는다.
다시 말하자면 객체의 내용은 변경할 수 있다.
2.2 Objet.freeze
- Object.freeze()를 사용하여 불변(immutable) 객체로 만들수 있다.
객체 내부의 객체는 얼리지 못한다.
1 | const user1 = { |
- 하지만 객체 내부의 객체(Nested Object)는 변경가능하다.
- 즉 메소드의 내부내용은 변경가능하다.
1 | const user = { |
- 내부 객체까지 변경 불가능하게 만들려면 Deep freeze를 하여야 한다.
1 | function deepFreeze(obj) { |
2.3 Immutable.js
또 다른 대안으로 Facebook이 제공하는 Immutable.js를 사용하는 방법이 있다.
Immutable.js는 List, Stack, Map, OrderedMap, Set, OrderedSet, Record와 같은 영구 불변 (Permit Immutable) 데이터 구조를 제공한다.
- npm으로 설치한다.
1
npm install immutable
- Immutable.js의 Map 모듈을 임포트하여 사용한다.
1
2
3
4
5const { Map } = require('immutable')
const map1 = Map({ a: 1, b: 2, c: 3 })
const map2 = map1.set('b', 50)
map1.get('b') // 2
map2.get('b') // 50- map1.set(‘b’, 50)의 실행에도 불구하고 map1은 불변하였다. map1.set()은 결과를 반영한 새로운 객체를 반환한다.