# 내부슬롯(internal slot)과 내부 메서드(internal method)
EMCAScript 사양에 등장하는 이중 대괄호 ([[...]])로 감싼 이름들이 내부 슬롯과 내부 메서드이다.
자바스크립트 엔진에서 실제로 동작하지만 개발자가 직접 접근할 수 있도록 외부로 공개된 객체의 프로퍼티는 아니다.
원칙적으로 자바스크립트 내부슬롯과 내부 메서드에 직접적으로 접근하거나 호출할 수 있는 방법을 제공하지는 않으나, 일부 내부 슬롯과 일부 메서드에 한하여 간접적으로 접근할 수 있다.
모든 객체는 [[Prototype]]이라는 내부 슬롯을 갖는다. 내부 슬롯은 자바스크립트 엔진의 내부로직이므로 원칙적으로 직접 접근을 할 수 없지만 __proto__ 를 통해 간접적으로 접근가능하다.
const obj = {};
// 내부 슬롯은 자바스크립트 엔진의 내부로직이므로 직접 접근할 수 없다
console.log(obj.[[Prototype]];) // Uncaught SyntaxError: Unexpected token '['
// 일부 내부 슬롯과 내부 메서드에 한하여 간접적으로 접근할 수 있다
console.log(obj.__proto__); // Object.prototype 출력
# 프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체
자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의한다.
- 프로퍼티 상태
1) 프로퍼티의 값(value)
2) 값의 갱신 가능 여부(writable)
3) 열거 가능 여부(enumerable)
4) 재정의 가능 여부(configurable)
프로퍼티 어트리뷰트는 자바스크립트 엔진이 관리하는 내부 상태 값(meta-property)인 내부 슬롯 [[value]], [[Writable]], [[Enumerable]], [[Configurable]] 이다. 이러한 프로퍼티 어트리뷰트에 직접 접근할 수 없지만 Object.getOwnPropertyDescriptor 메서드를 사용하여 간접적으로 확인할 수 있다.
- 첫번째 매개변수: 객체의 참조를 전달
- 두번째 매개변수: 프로퍼티 키를 문자열로 전달
- 반환값: 프로퍼티 디스크립터(PropertyDescriptor)객체
- 존재하지 않는 프로퍼티나 상속받은 프로퍼티에 대한 프로퍼티 디스크립터를 요구하면 undefinne 반환
- 하나의 프로퍼티에 대해 프로퍼티 디스크립터 객체 반환
const person = {
name: 'Lee'
};
// 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 티스크립터 객체를 반환
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
Object.getOwnPropertyDescriptors: 모든 프로퍼티의 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크립터 객체들을 반환
# 데이터 프로퍼티와 접근자 프로퍼티
프로퍼티는 데이터 프로퍼티와 접근자 프로퍼티로 구분할 수 있다
- 데이터 프로퍼티 (data property)
- 키와 값으로 구성된 일반적인 프로퍼티. 거의 모든 프로퍼티는 데이터 프로퍼티이다. - 접근자 프로퍼티 (accessor property)
- 자체적으로는 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 호출되는 접근자 함수(accessor function)로 구성된 프로퍼티
## 데이터 프로퍼티
데이터 프로퍼티는 다음과 같은 4가지 프로퍼티 어트리뷰트를 갖는다.
프로퍼티 어트리뷰트 | 프로퍼티 디스크립터 객체의 프로퍼티 | 설명 |
[[Value]] | value | - 프로퍼티 키를 통패 프로퍼티 값에 접근하면 반환되는 값 |
[[Writable]] | wirtable | - 프로퍼티 값으 변경 가능 여부를 나타내며 불리언 값을 갖는다. - false: 해당 프로퍼티의 [[value]] 값을 변경할 수 없는 읽기 전용 프로퍼티가 된다 |
[[Enumerable]] | enumerable | - 프로퍼티의 열거 가능 여부를 나타내며 불리언 값을 갖는다. - false: 해당 프로퍼티는 for...in 문이나 Obejct.keys 메서드 등으로 열거X |
[[Configurable]] | configuerable | - 프로퍼티의 재정의 가능 여부를 나타내며 불리언 값을 갖는다. - false: 프로퍼티의 삭제, 프로퍼티 어트리뷰트의 값의 변경 금지 (단, [[Writable]]이 true인 경우 [[value]] 의 변경과 [[Wirtable]]을 false로 변경하는 것은 허용된다.) |
프로퍼티가 생성될 때 [[Value]]의 값은 프로퍼티 값으로 초기화되며 [[Writable]], [[Enumerable]], [[Configurable]] 값은 true 로 초기화된다.
## 접근자 프로퍼티 (accessor property)
접근자 프로퍼티는 자체적으로 값을 갖지 않고 다른 데이터 프로퍼티의 값을 읽거나 저장할 때 사용하는 접근자 함수(accessor function)로 구성된 프로퍼티다.
프로퍼티 어트리뷰트 | 프로퍼티 디스크립터 객체의 프로퍼티 | 설명 |
[[Get]] | get | - 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 읽을 때 호출되는 접근자 함수이다. - 접근자 프로퍼티 키로 프로퍼티 값에 접근하면 프로퍼티 어트리뷰트 [[Get]]의 값, 즉 getter 함수가 호출되고 그 결과가 프로퍼티 값으로 반환된다. |
[[Set]] | set | - 접근자 프로퍼티를 통해 데이터 프로퍼티의 값을 저장할 때 호출되는 접근자 함수이다. - 접근자 프로퍼티 키로 프로퍼티 값을 저장하면 프로퍼티 어트리뷰트 [[Set]]의 값, 즉 setter 함수가 호출되고 그 결과가 프로퍼티 값으로 저장된다. |
[[Enumerable]] | enumerable | - 데이터 프로퍼티의 [[Enumerable]] 와 같음 |
[[Configurable]] | configurable | - 데이터 프로퍼티의 [[Configurable]] 와 같음 |
접근자 함수 = getter/setter 함수
접근자 함수는 getter와 setter 함수를 모두 정의할 수도 있고 하나만 정의할 수도 있다.
접근자 프로퍼티는 자체적으로 값(프로퍼티 어트리뷰트 [[Value]])을 가지지 않으며, 데이터 프로퍼티의 값을 읽거나 저장할 때 관여할 뿐이다.
# 프로퍼티 정의
프로퍼티 정의란 새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것을 말한다.
프로퍼티 어트리뷰트를 정의하는 방법: Object.defineProperty 메서드를 사용
- writable
- false인 경우 해당 프로퍼티의 [[Value]] 값을 변경할 수 없다
- 값을 변경하면 에러는 발생하지 않고 무시된다.
ex) person.lastName = 'Kim' 으로 변경해도 에러는 발생하지 않고 무시됨 - enumerable
- false인 경우 for...in 문이나 Object.keys 등으로 열거할 수 없다 - configurable
- false인 경우 해당 프로퍼티를 삭제할 수 없다 => 프로퍼티 삭제하면 에러는 발생하지 않고 무시된다.
- false인 경우 해당 프로퍼티를 재정의할 수 없다 => 에러 발생
// 데이터 프로퍼티 정의
Object.defineProperty(person, 'firstName', {
value: 'Kim',
writable: true,
enumerable: true,
configurable: true
});
Object.defineProperty(person, 'lastName',{
value: 'Lee'
});
let descriptor = Object.getOwnPropertyDescriptor(person, 'firstName');
console.log('firstName : ', descriptor);
// 디스크립터 객체의 프로퍼티를 누락시키면 undefined, false가 기본값이다.
descriptor = Object.getOwnPropertyDescriptor(person, 'lastName');
console.log('lastName : ', descriptor);
// lastName의 enumerable 이 false이므로 열거되지 않는다.
console.log(Object.keys(person)); // [ 'firstName' ]
여러 개의 프로퍼티를 한 번에 정의하고 싶다면 Object.defineProperties 메서드를 사용하면 된다.
const person = {};
Object.defineProperties(person, {
// 데이터 프로퍼티 정의
firstName: {
value: 'Kim',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
value: 'Nana',
writable: true,
enumerable: true,
configurable: true
},
// 접근자 프로퍼티 정의
fullName: {
// getter 함수 (
get() {
return `$this.firstName $this.lastName`;
},
// setter 함수
set(name) {
[this.firstName, this.lastName] = name.split(' ');
},
enumerable: true,
configurable: true
}
});
person.fullName = 'Gildong Hong';
console.log(person);
# 객체 변경 방지
객체는 변경 가능한 값이므로 재할당 없이 직접 변경할 수 있다.
- 프로퍼티 추가, 삭제, 값 갱신 가능
- Object.definnProperty 메서드를 사용하여 프로퍼티 어트리뷰트 재정의 가능
자바스크립트는 객체의 변경을 방지하는 다양한 메서드를 제공한다.
Shallow Only(얕은 변경 방지) | 구분 | 메서드 | 설명 |
객체 확장 금지 | Object.preventExtensions | 프로퍼티 추가 불가능 - 프로퍼티 삭제는 가능 |
|
객체 밀봉 | Object.seal | 프로퍼티 값 읽기, 프로퍼티 값 쓰기만 가능 - 프로퍼티 값 갱신 가능 |
|
객체 동결 | Object.freeze | 프로퍼티 값 읽기만 가능 |
## 불변 객체
Object.preventExtensions, Object.seal, Object.freeze 는 얕은 변경 방지(Shallow Only)로 직속 프로퍼티만 변경이 방지되고 중첩객체까지에는 영향을 주지 않는다.
중첩객체까지 영향을 미치려면 재귀적으로 위의 메서드를 호출해야 한다.
'JavaScript' 카테고리의 다른 글
[JavaScript] 함수 - 참조에 의한 전달과 외부 상태의 변경 (2) | 2023.10.27 |
---|---|
[JavaScript] 함수 - 함수 호출 (2) | 2023.10.27 |
[JavaScript] 함수 - Funtion 생성자 함수, 화살표 함수(개념만) (0) | 2023.10.27 |
[JavaScript] 함수 - 함수 생성 시점과 함수 호이스팅 (0) | 2023.10.27 |
[JavaScript] 함수 - 함수를 사용하는 이유, 함수 리터럴, 함수 정의 (0) | 2023.10.23 |