객체의 프로퍼티는 플래그 (flag) 라는 속성 세 가지를 갖습니다.
각 플래그는 true 아니면 false 의 값을 갖고, 이에 따라 프로퍼티의 속성이 정해집니다.
writable: 값을 수정할 수 있는지enumerable: 반복문을 사용하여 열거할 수 있는지 (false 면 반복문에 포함 ❌)configurable: 프로퍼티 삭제나 플래그 수정이 가능한지일반적인 방법으로 생성한 프로퍼티의 플래그는 모두 true 의 값을 가집니다.
Object.defineProperty(obj, propertyName, descriptor)
defineProperty 를 사용하여 프로퍼티 플래그, 값 등을 설정할 수 있습니다.
let user = {
name: "John"
};
Object.defineProperty(user, "name", {
writable: false
});
user.name = "Pete"; // Error: Cannot assign to read only property 'name'
defineProperties 를 사용하면 여러 개의 프로퍼티를 한 번에 설정 가능합니다.
Object.defineProperties(obj, {
prop1: descriptor1,
prop2: descriptor2
// ...
});
객체 프로퍼티는 두 종류로 나뉩니다.
getter, setter 역할을 하나 외부에서는 일반적인 프로퍼티처럼 사용접근자 프로퍼티를 사용하여 객체의 프로퍼티에 접근하는 방법을 알아봅시다.
접근자 프로퍼티는 객체 리터럴 안에 get 과 set 을 붙여 나타냅니다.
일반 메서드처럼 () 을 붙여 실행하지 않고, 이름만 가져다 사용해도 내부의 코드를 실행합니다.
let obj = {
get propName() {
// getter, obj.propName을 실행할 때 실행되는 코드
},
set propName(value) {
// setter, obj.propName = value를 실행할 때 실행되는 코드
}
};
getter 는 프로퍼티를 반환하고, setter 는 인수를 넘겨받아 프로퍼티에 값을 설정합니다.
인수는 obj.propName = value 처럼 값을 할당하는 모양새로 넘겨줍니다.
student 라는 객체에 이름을 설정하고 가져오는 getter, setter 를 만들었습니다.
둘은 호출한 객체의 name 프로퍼티를 반환하거나 인수를 받아 값으로 설정합니다.
let student = {
get name(){
return this.name;
},
set name(value){
this.name = value;
}
}
student.name = "JJ";
그런데, 위 코드를 실행하면 Maximum call stack size exceede 라는 에러가 발생합니다.
이 에러는 콜 스택의 최대 사이즈를 초과하여 발생하는데요, 콜 스택은 실행중인 함수의 정보가 위치하는 공간입니다.
이런 콜 스택이 꽉 찼다는 건 실행중인 함수가 너무 많다는 뜻이겠죠??
여기 존재하는 함수라고는 getter, setter 두 개밖에 없는데...
그러나 하나의 함수는 여러 번 재차 호출될 수 있고, 호출마다 새로운 실행 컨텍스트를 생성하여 콜 스택의 공간을 차지합니다.
따라서, getter 와 setter 가 계속해서 재귀호출 되고있다 볼 수 있습니다❗
❓ 재귀호출하는 코드가 없는데??
물론 코드 내부에서 재귀를 수행하는 명시적인 부분은 없지만, getter 와 setter 의 등장 자체가 재귀를 야기합니다.
우리가 접근자 프로퍼티를 사용하면, student.name 이 get name() 을 실행하고 student.name = "JJ" 가 set name(value) 를 실행합니다.
근데, 이와 같은 동작이 접근자 프로퍼티 내의 코드인 this.name, this.name = value 에서도 동일하게 일어납니다.
즉, getter 와 setter 내부에서 다시 getter 와 setter 를 호출하는 재귀가 발생하는 겁니다.
그렇게 부른 함수는 또 자기 자신을 호출하는 무한 재귀 현상에 빠집니다.
콜 스택 사이즈 초과 에러가 나는 이유는 그 때문입니다.
🦕 오류는 어떻게 해결할까?
근본적으로 이 오류는 접근자 프로퍼티 내의 this.name 의 이 name 이 일반적인 데이터 프로퍼티가 아니라 접근자 프로퍼티로 취급된다는 점에서 발생합니다.
따라서, 접근자 프로퍼티로 취급되지 않고 데이터 프로퍼티로 취급되도록 고쳐주면 됩니다.
let student = {
get name(){
return this._name;
},
set name(value){
this._name = value;
}
}
위처럼... name 이 아니라 _name 을 사용합니다.
이러면 접근자 프로퍼티 내부에서는 재귀가 일어나지 않고 _name 이라는 데이터 프로퍼티에 접근하는 방식으로 getter 와 setter 의 동작을 수행합니다.
_name 에 값을 저장하고, 접근은 getter 와 setter 를 이용하는 것이죠.
이러면 student 에는 _name 이라는 프로퍼티가 생기고, student._name 으로 직접 접근도 가능합니다.
그러나 _ 등이 붙은 프로퍼티는 내부에서만 활용하고 외부에서는 접근하지 않는 게 관습입니다.