객체를 immutable 하게 만드는 방법
객체를 변경 할 수 없게 만드는 방법에는 대표적으로 Object.freeze()가 있다. 하지만, 이 방법 외에도 몇 가지 방법들이 있다.
- constvs- Object.freeze()- immutable한 상태를 만드는 방법은- const와- Object.freeze()가 있는데 둘의 차이를 살펴보면 아래와 같다.
 
- const: 참조값을 못 바꾸게 함 (객체 내부의 프로퍼티의 값은 변경이 가능)
- Object.freeze(): 값 자체를 못 바꾸게 함 (참조 값을 재할당 가능)
- 위의 두 방법을 같이 사용하면 되지만, - Nested Object는 까지는 변경을 막지 못함 ->- deep freeze로 해결 가능
조금 더 자세히, 다양한 방법에 대해 알아보자.
Object.defineProperty()
- Object객체의- static method로 객체에 직접 새로운 속성을 정의하거나, 이미 존재하는 속성을 수정하고, 그 객체를 반환한다.
| 1 | Object.defineProperty(obj, prop, descriptor); | 
- obj: 속성을 정의할 객체
- prop: 새로 정의하거나 수정할 속성
- descriptor: 새로 정의하거나 수정할 속성을 기술하는 객체
descriptor 객체에 포함되는 속성
- configurable: 해당 속성의 값을 변경할 수 있고, 대상 객체에서 삭제할 수도 있다면- true- → false가 된다면configurable과enumerable의 플래그를 수정할 수 없고,writable: false의 경우true로 바꿀 수 없다. (true → false는 가능)value는 변경이 가능.
- 즉, 해당 속성의 설정 값을 변경할 수 있는지를 관리하는 속성이다.
- 기본값 : false
 
- → 
- enumerable: 해당 속성이 대상 객체의 속성 열거 시 노출된다면- true- 기본값 : false
 
- 기본값 : 
- value: 속성에 연관된 값. 아무 유효한 JavaScript 값(숫자, 객체, 함수 등)이나 가능- 기본값 : undefined
 
- 기본값 : 
- writable: 할당 연산자(- =)로 속성의 값을 바꿀 수 있다면- true- 기본값 : false
 
- 기본값 : 
Object.getOwnPropertyDescriptor
- 객체의 해당 속성의 descriptor를 확인 할 수 있다.
| 1 | const user1 = { | 
Object.preventExtensions()
- 자바스크립트의 모든 객체는 [[Extensible]]이라는 숨겨진 속성을 가지고 있는데, 이 속성의 값이false면 객체에 속성을 추가하는 것이 불가능하다. (기본값 :true)
- 이 속성을 false로 변경하는 메소드가Object.preventExtensions(obj)이다.- 객체의 [[Extensible]]속성은Object.isExtensible(obj)라는 메소드로 확인할 수 있다.
 
- 객체의 
- 개체의 기존 속성을 변경할 수 있음
- 새로운 속성 추가 방지
- [[Extensible]]속성을- false로 변경한다.
| 1 | const user1 = { | 
Object.preventExtensions(user1) 을 통해 내부의 Extensible 속성이 false로 변경된 모습을 볼 수 있고, 이후에 Object.defineProperty()로 새로운 속성을 추가하려고 하면 위와 같은 에러를 발생시키게 된다.
Object.seal()
- 개체의 기존 속성을 변경할 수 있음
- 새로운 속성 추가 방지
- 기존 속성 제거 허용 안함
- [[Extensible]]속성을- false로 변경한다.
- 모든 프로퍼티의 configurable속성을false로 변경한다.
| 1 | const user1 = { | 
Object.seal()이 적용된 이후 Extensible과 configurable이 모두 false로 변경된 것을 확인할 수 있다. 또한, delete 연산자를 통해 user1의 name과 age를 각각 Object.seal() 이전, 이후에 삭제를 시도한 모습인데, Object.seal() 이전에 수행된 name 속성은 삭제된 반면, 이후에 실행된 age 속성은 삭제되지 않은 것을 볼 수 있다.
하지만 Object.seal() 이후에도 writable 속성이 true인 경우에는 객체의 속성은 변경이 가능하다.
Object.freeze()
- 객체를 불변하게 만드는 가장 강력한 방법이다.
- 새로운 속성 추가 방지
- 기존 속성 제거를 허용하지 않음
- 속성을 변경할 수 없음 (depth1단계만)
- 자식 개체의 속성을 수정할 수 있음
- [[Extensible]]속성을- false로 변경한다.
- 모든 프로퍼티의 configurable속성을false로 변경한다.
- 모든 프로퍼티의 writable속성을false로 변경한다.
| 1 | const user1 = { | 
Object.seal()과는 다르게 Object.freeze()를 수행하고 나면, writable이 false로 변경되고 이후 객체의 속성을 수정해도 변경되지 않는 것을 볼 수 있다.
또한, Object.freeze() 는 Object.defineProperty() 와 Object.preventExtensions() 로 구현이 가능하다.
| 1 | const _freeze = function (obj) { | 
위와 같이 Object.defineProperty() 를 통해 writable속성과 configurable속성을 false 로 변경하고, Object.preventExtensions() 를 통해 [[Extensible]] 속성을 false 로 변경하면 된다.
하지만, Object.freeze()만으로는 객체 내부의 객체까지는 변경을 막을 수 없다. 이를 막으려면 deepFreeze를 구현해야 하는데, 아래와 같은 방법으로 구현할 수 있다.
deepFreeze
- Object.freeze()사용
| 1 | const deepFreeze = function (obj) { | 
- Object.defineProperty() + Object.preventExtensions()사용
| 1 | const deepFreeze = function (obj) { | 
비교
- 각각의 static method를 적용한 뒤에 객체의 상태가 어떻게 되는지 비교한 표이다.
| 기능 | default | preventExtensions | seal | freeze | 
|---|---|---|---|---|
| 새로운 속성 추가( [[Extensible]]) | ⭕️ | ⭕️ | ❌ | ❌ | 
| 기존 속성 삭제( configurable) | ⭕️ | ⭕️ | ❌ | ❌ | 
| 기존 속성 변경( writable) | ⭕️ | ⭕️ | ⭕️ | ❌ | 
참조
https://velog.io/@kdo0129/객체-잠그기
https://til.cybertec-postgresql.com/post/2019-10-11-Object-preventExtension-vs-seal-vs-freeze/
https://ko.javascript.info/property-descriptors
https://helloworldjavascript.net/pages/240-object-in-depth.html
