객체에서는 key:value 쌍으로 프로퍼티가 저장되며
각각을 key : 프로퍼티 이름, value : 프로퍼티 값 으로도 부른다.
즉 key:value의 한 쌍을 프로퍼티라고 생각하면 된다.
객체 프로퍼티는 값(value) 과 함께 플래그(flag)라 불리는 특별한 속성 세 가지로 구성된다.
값 수정 가능성.
true일 시 값을 수정할 수 있으며, false일 때는 읽기만 가능하다.
나열 가능성
true일 시 반복문 등을 통해 나열이 가능하며, false일 때는 나열이 불가능하다.
설정 가능성.
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가지 플래그들의 효과에 대해 알아보겠다.
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' 값이 변경되지 않았음을 볼 수 있다.
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 플래그에 따라 동작하는 것이다.
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가 만들어내는 제약사항은
코드에서 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를 반환한다.
근데 위 메서드들은 실무에선 잘 사용되지 않는다고 한다.ㅎㅎ,,