객체를 immutable
하게 만드는 방법
객체를 변경 할 수 없게 만드는 방법에는 대표적으로 Object.freeze()
가 있다. 하지만, 이 방법 외에도 몇 가지 방법들이 있다.
const
vsObject.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()
- 객체를 불변하게 만드는 가장 강력한 방법이다.
- 새로운 속성 추가 방지
- 기존 속성 제거를 허용하지 않음
- 속성을 변경할 수 없음 (
depth
1단계만) - 자식 개체의 속성을 수정할 수 있음
[[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