configuration은 리눅스 운영체제를 처음 접했을 때 때 참 많이 마주치던 단어다. 객체의 속성도 configure-able 하다니..
일반적으로는 드러나지 않지만, 객체의 속성에는 3가지 설정사항이 있다. 이것들을 flags라고 부른다.
세 가지 모두 기본값은 true이다.
writable : 값이 수정 될 수 있는지 말한다.enumerable : loop의 요소로 쳐줄지 아닐지 말한다.configurable : 속성이 삭제 될 수 있는지, 그리고 이 flags들이 수정 될 수 있는지 말한다.객체의 속성, 이 속성의 flags와 더불어 value 까지 4가지 속성을 가진 객체를 descriptor라고 부른다. 이것을 통해 flags에 접근하고 수정 할 수 있다. 확인하는(읽는) 방법은 아래와 같다.
// Object.getOwnPropertyDescriptor(obj, propertyName);
// 위 메소드는 obj의 propertyName 속성의 descriptor를 반환한다.
let user = {
name: "John"
};
const descriptor = Object.getOwnPropertyDescriptor(user, 'name');
console.log(descriptor);
// { value: "John",
// writable: true,
// enumerable: true,
// configurable: true }
descriptor를 수정하려면 다른 메소드가 필요하다. 아래 예시를 통해 확인해 봐라. 상연아 집중해라
Object.defineProperty(user, 'name', {
value: 'Kim',
writable: false
})
예시에서 보다시피 값 자체도 바꿀 수 있다.
flags의 설정을 무시하는 구문을 실행하면 오류가 발생할까?
- strict mode : 오류가 발생하고 프로세스가 멈춘다.
- non-strict mode : 오류는 발생하지 않지만 구문은 무시된다.
enumerable : false인 경우 for ..in 구문이나 Object.key 메소드에서 제외 된다.
configurable은 one-way road다. 한번 false로 만든 뒤에는 삭제도, configure도 할 수 없다. 그러나 값은 여전히 수정 할 수 있다. writable 플래그가 참이라면 말이다.
헌데 configurable : false 일 때에도 flag를 딱 한 가지 경우엔 수정 할 수 있는데, wrtiable : true -> flase인 경우다 . 이 경우는 값을 한층 더 보호하는 행위라서 허용된다고 이해 할 수 있다. 물론 그 반대는 불가능하다.
일반적인 방법으로 객체를 복사하면
flags까지 복사되지 않는다. 이것이 필요하다면 아래처럼 가능하다.// 메소드들이 복수형인 것에 주목한다. 각 메소드는 배열을 리턴하고 배열을 매개변수로 받는다. let clone = Object.defineProperties({}, Object.getOwnPropertyDescriptors(obj));
지금까지 어떤 객체에 속한 각각의 속성을 관리하는 방법들이었다. 당연히 객체 자체를 관리하는 방법도 있다. 실제로 사용하는 경우는 거의 없지만 원체 간단하니 읽어나 두자.
Object.preventExtensions(obj)
: Forbids the addition of new properties to the object.
Object.seal(obj)
: Forbids adding/removing of properties. Sets configurable: false for all existing properties.
Object.freeze(obj)
: Forbids adding/removing/changing of properties. Sets configurable: false, writable: false for all existing properties.
위 상태들을 확인 해 볼 수도 있다.
Object.isExtensible(obj)
: Returns false if adding properties is forbidden, otherwise true.
Object.isSealed(obj)
: Returns true if adding/removing properties is forbidden, and all existing properties have configurable: false.
Object.isFrozen(obj)
: Returns true if adding/removing/changing properties is forbidden, and all current properties are configurable: false, writable: false.
우리가 흔히 보는 객체의 속성은 data property이다. 좀 더 정확히 말해보자면 descriptor가
로 구성된 속성이다.
그에 반해 accessor property descriptor는 다음과 같이 구성되어 있다.
예를 들면 아래와 같다.
let user = {
name: "John",
surname: "Smith"
};
Object.defineProperty(user, 'fullName', {
get() {
return `${this.name} ${this.surname}`;
},
set(value) {
[this.name, this.surname] = value.split(" ");
}
});
console.log(user.fullName); // John Smith
for(let key in user) console.log(key); // name, surname
getter와setter는 함수이기 때문에 조건을 다는 등 유연하게 쓸 수도 있다.let user = { get name() { return this._name; }, set name(value) { if (value.length < 4) { console.log("최소 4글자 이상으로 할당하세요."); return; } this._name = value; } }; user.name = 'Kim' // undefined, 최소 4글자 이상으로 할당하세요.위 예시에서
user._name속성에도 접근 할 수 있다. 그러나 널리 알려진 관습의 하나로, 객체 안에 underscore("_")로 이름 짓는 속성은 일종의내부속성으로 여기고 외부에서 접근하지 않는다.getter를 이용하도록 하자.