[javascript] 프로퍼티 플래그와 설명자

skdus·2022년 5월 8일
0

JavaScript

목록 보기
7/17
post-thumbnail

💡 객체 프로퍼티란?

객체에서는 key:value 쌍으로 프로퍼티가 저장되며
각각을 key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다.
즉 key:value의 한 쌍을 프로퍼티라고 생각하면 된다.


💡 프로퍼티 플래그

객체 프로퍼티는 값(value) 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지로 구성된다.

writable

값 수정 가능성.
true일 시 값을 수정할 수 있으며, false일 때는 읽기만 가능하다.

enumerable

나열 가능성
true일 시 반복문 등을 통해 나열이 가능하며, false일 때는 나열이 불가능하다.

configurable

설정 가능성.
true일 시 프로퍼티 삭제 / 플래그 수정이 가능하며, false일 때는 프로퍼티 삭제 플래그 수정이 불가능하다.

💡 프로퍼티 플래그 확인하기

객체의 프로퍼티들은 각각 플래그를 가지고 있다.

기본적으로 리터럴{}로 생성한 객체에서는, 각각 프로퍼티 플래그는 모두 true 값을 가진다. 이렇게 true로 설정된 플래그는 언제든 수정할 수 있다.

특정 프로퍼티에 대한 정보를 얻기 위해서는
Object.getOwnPropertyDescriptor를 사용하며,

메서드의 인자로 (대상 객체, 객체 내 프로퍼티 이름)을 넣어주면 된다.

const obj = {
  name : 'nayeon',
  age : 24,
};

Object.getOwnPropertyDescriptor(obj, 'name');

getOwnPropertyDescriptor 메서드의 결과로 새로운 객체가 리턴되는데, 이를 프로퍼티 설명자(descriptor)라고 한다.

그림과 같이 프로퍼티 설명자는 프로퍼티 값과 3가지 프로퍼티 플래그의 불린값을 담고 있다.

각 프로퍼티마다 이 프로퍼티 설명자가 존재하며, 플래그나 프로퍼티 값을 변경하면 설명자의 값이 변경된다.


💡 프로퍼티 값, 플래그 변경하기

Object.definedProperty로 프로퍼티 값과 플래그를 변경할 수 있으며,
메서드의 인자로는 (대상 객체, 프로퍼티 이름, 적용하고자 하는 프로퍼티 설명자)을 넣어준다.

이 때 변경하려는 프로퍼티가 있다면 변경을 수행하며,
프로퍼티가 없다면 인수로 넘겨받은 정보를 이용해 프로퍼티를 생성하며 플래그 값은 기본적으로 false가 된다.

const obj = {
  name:'Olaf'
};

Object.defineProperty(obj, 'name', {value:'Elsa'});
Object.getOwnPropertyDescriptor(obj, 'name');

const obj2 = {};
Object.defineProperty(obj2, 'name', {value:'Anna'});
Object.getOwnPropertyDescriptor(obj2, 'name');

obj는 프로퍼티 name을 가지고 있으며, obj2는 프로퍼티 name를 가지고 있지 않다.

obj는 설명자에서 value 값만 바뀌었으며
obj2는 설명자에서 value 값을 생성하면서 플래그값이 모두 false로 세팅되었다.

obj2.name = 'Anna';

위와 같은 방식으로 새로운 프로퍼티를 만들면 플래그 기본값이 true이지만, definedProperty 메서드를 이용하면 플래그 기본값은 false임을 주의한다.

만약 definedProperty를 이용하면서 플래그를 true로 설정하고 싶다면, 3번째 인자값에 추가로 부여하면 된다.

Object.defineProperty(obj2, 'name', {
  value:'Anna', 
  writable:true, 
  enumerable:true, 
  configurable:true
});

💡 플래그 살펴보기

이제 3가지 플래그들의 효과에 대해 알아보겠다.

Writable

true일 시 값을 수정할 수 있으며, false일 때는 읽기만 가능하다.

const obj = {
  name:'Anna'
};

Object.defineProperty(obj, 'name', {writable:false} );
obj.name = 'Elsa';
obj;

obj 객체의 name 프로퍼티의 '값 수정 가능성'을 false로 변경하여 읽기만 가능하다.

writable:false 속성은 'strict mode'에서만 오류를 발생시킨다.

그래서 원래대로라면 obj.name='Elsa' 코드에서 오류를 발생시켜야 하지만,
'sloppy mode'를 기본으로 하는 브라우저(크롬)의 콘솔에서 작업했기 때문에, 변경 작업은 무시되었다.

아무튼 obj 객체를 조회했을 때, name:'Anna' 값이 변경되지 않았음을 볼 수 있다.

Enumerable

ture일 시 반복문 등을 통해 나열이 가능하며, false일 때는 나열이 불가능하다.

객체의 프로퍼티를 나열하기 위해서, for in 문을 사용해본다.

const obj = {
  name:'nayeon',
  age:24,
  hello(){
    return 'world'
  },
}

for(let i in obj){
  console.log(i);
}

Object.defineProperty(obj, 'hello', {enumerable:false});
for(let i in obj){
  console.log(i);
}

반복문에서는 enumerable:false인 요소를 불러오지 않는다.

그러므로 두번째 for 문에서는, hello 프로퍼티가 조회되지 않는다.

alert(Object.keys(obj)); //name, age

주의할 점으로는
Object.keys를 이용해 프로퍼티 키들을 배열로서 얻고자 할 때
enumerable:false인 값은 역시 조회되지 않음에 유의한다.

entries, keys, values 메서드 모두 열거를 통해 배열 원소에 키를 넣기 때문에,

enumarable 플래그에 따라 동작하는 것이다.

configurable

true일 시 프로퍼티 삭제 / 플래그 수정이 가능하며, false일 때는 프로퍼티 삭제 플래그 수정이 불가능하다.

configurable 플래그는 강력한 제약을 주므로, 더 주의해야 한다.

한 번 configurable 플래그를 false로 설정하면 해당 프로퍼티는 객체에서 지울 수 없다.

const obj = {};

Object.defineProperty(obj, 'age', {value:24});
Object.getOwnPropertyDescriptor(obj, 'age');
obj.age = 150;
obj;

delete obj.age;
obj;

Object.defineProperty(obj, 'age', {configurable:true});

configurable 옵션에 의해 해당 프로퍼티에 대한 삭제가 불가능하며, 프로퍼티의 플래그들도 모두 변경이 불가능하다.

정확히 말하면, configurable:false가 만들어내는 제약사항은

  • writable: false의 값을 true로 바꿀 수 없음 (true를 false로 변경하는 것은 가능함).
  • enumerable 플래그를 수정할 수 없음.
  • configurable 플래그를 수정할 수 없음
  • 접근자 프로퍼티 get/set의 변경이 불가능하며, 새롭게 생성하는 것은 가능하다고 한다.

코드에서 obj.age = 150; 동작이 수행되지 않는 것은, configurable 때문이 아니라 writable이 false이기 때문이다.
configurable 플래그가 false이더라도, writable 플래그가 true이면 프로퍼티 값을 변경할 수 있다. configurable은 프로퍼티 삭제 / 플래그 값 변경과 관련되어 있음에 유의하자.

여러 프로퍼티를 동시에 생성 / 변경하거나, 설명자를 조회하고 싶다면 각 메서드 뒷부분이 복수형이면 된다. => Object.definedproperties, Object.getOwnPropertyDescriptors


💡 객체 수정을 막아주는 다양한 메서드

프로퍼티 설명자는 특정 프로퍼티 하나를 대상으로 하는데,
아래 메서드를 사용하면 한 객체 내 프로퍼티 전체를 대상으로 하는 제약사항을 만들 수 있다.

Object.preventExtensions(obj) : 객체에 새로운 프로퍼티를 추가할 수 없게 한다.
Object.seal(obj) : 새로운 프로퍼티 추가나 기존 프로퍼티 삭제를 막아준다. 프로퍼티 전체에 configurable: false를 설정하는 것과 동일한 효과이다.
Object.freeze(obj) : 새로운 프로퍼티 추가나 기존 프로퍼티 삭제, 수정을 막아준다. 프로퍼티 전체에 configurable: false, writable: false를 설정하는 것과 동일한 효과이다.

아래 메서드는 위 세 가지 메서드를 사용해서 설정한 제약사항을 확인할 때 사용할 수 있다.

Object.isExtensible(obj)
새로운 프로퍼티를 추가하는 게 불가능한 경우 false를, 그렇지 않은 경우 true를 반환한다.
Object.isSealed(obj)
프로퍼티 추가, 삭제가 불가능하고 모든 프로퍼티가 configurable: false이면 true를 반환한다.
Object.isFrozen(obj)
프로퍼티 추가, 삭제, 변경이 불가능하고 모든 프로퍼티가 configurable: false, writable: false이면 true를 반환한다.

근데 위 메서드들은 실무에선 잘 사용되지 않는다고 한다.ㅎㅎ,,

-참고자료
https://ko.javascript.info/property-descriptors

0개의 댓글