JS-객체 프로퍼티 설정
| topics | 200-프론트개발 |
| types | 이론 학습 |
| tags | #javascript #object |
| references | developer.mozilla.org/ko/docs/Web/Jav... ko.javascript.info/object-properties |
JS 객체 프로퍼티는 어떻게 제어하나
객체의 프로퍼티는 단순히 value만 가지는 게 아니다. value + flag로 구성되어 있다.
왜 알아야 하나
일반적으로 객체를 만들 때는 그냥 obj.name = "value" 이런 식으로 쓴다. 근데 프레임워크나 라이브러리 코드를 보면, 특정 프로퍼티를 읽기 전용으로 만들거나, 삭제 불가능하게 만들어야 할 때가 있다. 이때 프로퍼티의 flag를 제어하면 된다.
Vue나 React의 내부 구현을 보면 이런 방식으로 프로퍼티를 제어한다. (JS-proxy&defineproperty 참고)
flag
writable–true이면 값을 수정할 수 있다. 그렇지 않으면 읽기만 가능enumerable–true이면 반복문을 사용해 나열할 수 있다 (for-in, Object.keys)configurable–true이면 프로퍼티 삭제나 플래그 수정이 가능. 그렇지 않으면:configurable플래그를 수정할 수 없음enumerable플래그를 수정할 수 없음writable: false의 값을true로 바꿀 수 없음 (true를false로 변경하는 건 가능)- 접근자 프로퍼티
get/set을 변경할 수 없음 (새롭게 만드는 건 가능)
일반적으로 모두 true가 기본값
Object.getOwnPropertyDescriptor()
특정 객체의 프로퍼티에 대한 정보를 모두 얻을 수 있다.
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);
obj - 객체
propertyName - key값
let user = {
name: "John"
};
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
alert( JSON.stringify(descriptor, null, 2 ) );
/* property descriptor:
{
"value": "John",
"writable": true,
"enumerable": true,
"configurable": true
}
*/
Object.defineProperty()
Object.defineProperty(obj, propertyName, descriptor)
descriptor - 적용하고자하는 프로퍼티의 속성
- 해당 프로퍼티가 있을 때
명시된 속성만 바꿔준다. - 해당 프로퍼티가 없을 때
명시되지 않은 속성은 다 false
let user = {};
Object.defineProperty(user, "name", {
value: "John"
});
let descriptor = Object.getOwnPropertyDescriptor(user, 'name');
/*
{
"value": "John",
"writable": false,
"enumerable": false,
"configurable": false
}
*/
프로퍼티 여러개 정의: Object.defineProperties(obj, descriptors)
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
그외의 객체 수정 방지 메서드
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를 반환.
접근자 프로퍼티
객체의 프로퍼티는 두 종류가 있다:
- 데이터 프로퍼티: 일반적인 프로퍼티
- 접근자 프로퍼티: 데이터 프로퍼티에 접근하기 위한 프로퍼티
접근자 프로퍼티(get/set 메서드를 가짐)나 데이터 프로퍼티(value를 가짐) 중 한 종류에만 속할 수 있다.
접근자 프로퍼티의 속성
get– 인수가 없는 함수로, 프로퍼티를 읽을 때 동작 (getter)set– 인수가 하나인 함수로, 프로퍼티에 값을 쓸 때 호출 (setter)enumerable– 데이터 프로퍼티와 동일configurable– 데이터 프로퍼티와 동일
사용 이유
값을 원하는대로 통제할 수 있다. 예를 들어, 사용자 이름이 4글자 이상이어야 한다는 제약을 걸고 싶을 때:
let user = {
get name() {
return this._name;
},
set name(value) {
if (value.length < 4) {
alert("입력하신 값이 너무 짧습니다. 네 글자 이상으로 구성된 이름을 입력하세요.");
return;
}
this._name = value;
}
};
user.name = "Pete";
alert(user.name); // Pete
user.name = ""; // 너무 짧은 이름을 할당하려 함
주의: defineProperty 안에 get과 value를 동시에 설정하면 에러가 난다.
get vs defineProperty
JS-클래스에서 get/set을 사용할 때와 defineProperty를 사용할 때의 차이점:
class Foo {
constructor () {
Object.defineProperty(this, 'bar', {
get () {
return this._bar;
},
set (value) {
this._bar = value;
}
});
}
get baz () {
return this._baz;
}
set baz (value) {
this._baz = value;
}
}
const myFoo = new Foo();
console.log('bar', myFoo.hasOwnProperty('bar')); // true
console.log('baz', myFoo.hasOwnProperty('baz')); // false
왜 이런 차이가 생기나?
class Example1 {
get hello() {
return 'world';
}
}
const obj = new Example1();
console.log(obj.hello);
// "world"
console.log(Object.getOwnPropertyDescriptor(obj, 'hello'));
// undefined
console.log(
Object.getOwnPropertyDescriptor(
Object.getPrototypeOf(obj), 'hello'
)
);
// { configurable: true, enumerable: false, get: function get hello() { return 'world'; }, set: undefined }
클래스에서 get과 set을 사용하면 prototype의 프로퍼티로 설정된다. 따라서 인스턴스 자체에는 해당 프로퍼티가 없고, 프로토타입에만 존재한다.
defineProperty를 constructor 안에서 사용하면 인스턴스 자체에 프로퍼티가 추가된다.
관련 문서
- JS-proxy&defineproperty - Proxy와 defineProperty의 차이
- JS-prototype - 프로토타입과 프로퍼티 상속
- JS-클래스 - 클래스와 접근자 프로퍼티