앞으로 살펴볼 프로퍼티 어트리뷰트를 이해하기 위해 먼저 내부 슬롯과 내부 메서드의 개념에 대해 알아보자. 내부슬롯과 내부 메서드는 자바스크립트 엔진의 구현 알고리즘을 설명하기 위해 ECMAScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드다. ECMAScript 사양에 등장하는 이중 대괄호로 감싼 이름들이 내부 슬롯과 내부 메서드다.
자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값인 내부 슬롯을 자동 정의한다.
○ [[value]] : 값.
○ [[writable]] : 값의 갱신 가능 여부.
○ [[enumerable]] : 열거 가능 여부.
○ [[configurable]] : 정의 가능 여부.
내부 슬롯에는 직접 접근할 수 없지만, Object.getOwnPropertyDesciptor 와 Object.getOwnPropertyDescriptors 메서드를 사용하여 확인할 수 있다.
const obj = {
name : 'kim',
hello(){console.log(`hello ${this.name}`)}
}
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
// {value: 'kim', writable: true, enumerable: true, configurable: true}
const obj = {
name : 'kim',
hello(){console.log(`hello ${this.name}`)}
}
console.log(Object.getOwnPropertyDescriptors(obj));
// hello : {writable: true, enumerable: true, configurable: true, value: ƒ}
// name : {value: 'kim', writable: true, enumerable: true, configurable: true}
접근자 함수는 getter/setter 함수라고도 부른다. 접근자 프로퍼티는 getter와 setter 함수를 모두 정의할 수도 있고, 하나만 정의할 수도 있다.
const person = {
firstName : 'Dan',
lastName : 'Kim',
get fullName(){
return `${this.firstName} ${this.lastName}.`;
},
set fullName(name){
[this.firstName, this.lastName] = name.split(' ');
}
}
person.fullName = 'Hyunsoo Kim';
console.log(person.fullName); //Hyunsoo Kim.
이를 내부 슬롯/메서드 관점에서 설명하면 다음과 같다. 접근자 프로퍼티 fullName으로 프로퍼티 값에 접근하면 내부적으로 [[Get]] 내부 메서드가 호출되어 다음과 같이 동작한다.
1. 프로퍼티 키가 유효한지 확인한다. 프로퍼티 키는 문자열 또는 심벌이어야 한다. 프로퍼티 키 'fullName' 은 문자열이므로 유효한 프로퍼티 키다.
2. 프로토타입 체인에서 프로퍼티를 검색한다. person 객체에 fullName 프로퍼티가 존재한다.
3. 검색된 fullName 프로퍼티가 데이터 프로퍼티인지 접근자 프로퍼티인지 확인한다. fullName 프로퍼티는 접근자 프로퍼티다.
4. 접근자 프로퍼티 fullName의 프로퍼티 어트리뷰트 [[Get]]의 값, 즉 getter 함수를 호출하여 그 결과를 반환한다.
접근자 프로퍼티의 어트리뷰트와 데이터 프로퍼티 어트리뷰트는 다르다.
○ 데이터 프로퍼티 어트리뷰트 : {value : .., writable : .., enumerable : .., configurable : ..}
○ 접근자 프로퍼티 어트리뷰트 : {get : f, set : f, enumerable : .., configurable : ,,}
Object.defineProperty와 Object.defineProperties는프로퍼티를 정의하면서 어트리뷰트도 같이 정의할 수 있다.
const person = {};
Object.defineProperty(person, 'name', {
value : 'Kim',
writable : true,
enumerable : true,
configurable : true,
});
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// {value: 'Kim', writable: true, enumerable: true, configurable: true}
Object.defineProperty(person, 'age', {
value : 30,
});
console.log(Object.getOwnPropertyDescriptor(person, 'age'));
//{value: 30, writable: false, enumerable: false, configurable: false}