[자바스크립트] 프로퍼티 어트리뷰트(Property Attribute)
본 내용은 이웅모 님의 모던 "자바스크립트 DeepDive" 책을 보고 정리한 내용입니다. 저작권 보호를 위해 책의 내용은 요약되었습니다.
내부슬롯과 내부 메서드
- 내부슬롯과 내부 메서드는 JavaScript 엔진의 구현 알고리즘을 설명하기 위해 ECMScript 사양에서 사용하는 의사 프로퍼티와 의사 메서드이다.
- JavaScript 엔진 내부적으로 사용되며, 일부 객체의 동작을 구현하는 데 사용된다.
- 일반적으로 내부 슬롯과 메서드는 JavaScript 코드에서 직접 접근할 수 없지만 내부 슬롯의 경우 __proto__를 통해 간접적으로 접근할 수 있다.
- 모든 객체는 [[Prototype]] 내부 슬롯을 가지며, 이는 객체의 프로토타입 체인을 구성하는 데 사용된다. 이 슬롯은 일반적으로 접근할 수 없지만, ES6에서는 Object.getPrototypeOf() 함수를 사용하여 간접적으로 접근할 수 있다.
- 내부 메서드에는 [[Get]], [[Set]], [[OwnPropertyKeys]] 등이 있고, 이들은 객체의 속성을 읽거나 쓰는 데 사용된다.
프로퍼티 어트리뷰트와 프로퍼티 디스크립터 객체
- JavaScript에서 객체 속성은 프로퍼티 어트리뷰트(Property Attribute)를 가지며, 이 어트리뷰트는 속성의 동작을 지정한다. 프로퍼티 어트리뷰트는 Object.defineProperty() 또는 Object.defineProperties() 함수를 사용하여 직접 설정할 수 있다.
- 프로퍼티 디스크립터 객체(Property Descriptor Object)를 사용하여 프로퍼티 어트리뷰트를 설정할 수 있는데 프로퍼티 디스크립터 객체는 속성의 정보를 담고 있는 객체로, 다음과 같은 속성을 가질 수 있습니다.
1. 데이터 프로퍼티(Data Property)
데이터 프로퍼티는 키와 값으로 구성된 일반적인 속성으로 값을 저장하고 가져오는 역할을 하며 프로퍼티 생성시 기본값으로 자동 정의된다.
네, 객체의 속성은 다음과 같이 정리할 수 있습니다.
[[Value]] | 속성의 키를 통해 값에 접근하면 반환되는 값이다. |
[[Writable]] | 속성의 값을 변경할 수 있는지 여부를 나타내며 boolean 값을 갖는다. |
[[Enumerable]] | 속성이 for...in 루프 등을 통해 열거 가능한지 여부를 나타내며 boolean 값을 갖는다. |
[[Configurable]] | 속성이 삭제하거나 속성 어트리뷰트를 변경할 수 있는지 여부를 나타내며 boolean 값을 갖는다. |
let person = {
name: 'John',
age: 30
};
2. 접근자 프로퍼티(Accessor Property)
접근자 프로퍼티는 데이터 프로퍼티와 달리 값을 직접 저장하지 않으며, 대신 값을 가져오는 getter와 값을 설정하는 setter 함수를 사용하여 값을 처리한다.
[[Get]] | 속성 값을 가져오는 getter 함수이다. |
[[Set]] | 속성 값을 설정하는 setter 함수이다. |
[[Enumerable]] | 속성이 for...in 루프 등을 통해 열거 가능한지 여부를 나타낸다. |
[[Configurable]] | 속성이 삭제하거나 속성 어트리뷰트를 변경할 수 있는지 여부를 나타낸다. |
let person = {
firstName: 'John',
lastName: 'Doe',
get fullName() {
return this.firstName + ' ' + this.lastName;
},
set fullName(name) {
let names = name.split(' ');
this.firstName = names[0];
this.lastName = names[1];
}
};
객체 변경 방지
1. 객체 확장 금지(Object.preventExtensions)
Object.preventExtensions 메소드는 객체에 새로운 프로퍼티를 추가할 수 없도록 설정한다. 하지만, 이미 존재하는 프로퍼티의 값은 변경할 수 있다. 이 때, [[Extensible]] 프로퍼티가 false로 변경된다.
let person = { name: 'John', age: 30 };
Object.preventExtensions(person);
person.gender = 'Male'; // 무시됨 (strict mode에서는 TypeError 발생)
console.log(person); // { name: 'John', age: 30 }
console.log(Object.isExtensible(person)); // false
2. 객체 밀봉(Object.seal)
Object.seal 메소드는 객체에 새로운 프로퍼티를 추가하거나, 이미 존재하는 프로퍼티의 속성을 변경하거나 삭제할 수 없도록 설정한다. 이 때, [[Extensible]] 프로퍼티가 false로 변경되고, 모든 존재하는 프로퍼티의 [[Configurable]] 프로퍼티가 false로 변경되므로 밀봉된 객체는 읽기와 쓰기만 가능하다.
let person = { name: 'John', age: 30 };
Object.seal(person);
delete person.age; // 무시됨 (strict mode에서는 TypeError 발생)
person.gender = 'Male'; // 무시됨 (strict mode에서는 TypeError 발생)
person.name = 'Mike'; // 가능
console.log(person); // { name: 'Mike', age: 30 }
console.log(Object.isSealed(person)); // true
3. 객체 동결(Object.freeze)
Object.freeze 메소드는 객체에 새로운 프로퍼티를 추가하거나, 이미 존재하는 프로퍼티의 속성을 변경하거나 삭제할 수 없도록 설정하고, 존재하는 모든 프로퍼티의 [[Writable]] 프로퍼티를 false로 변경한다. 이 때, [[Extensible]] 프로퍼티가 false로 변경되고, 모든 존재하는 프로퍼티의 [[Configurable]] 프로퍼티가 false로 변경되므로 동결된 객체는 읽기만 가능하다.
let person = { name: 'John', age: 30 };
Object.freeze(person);
delete person.age; // 무시됨 (strict mode에서는 TypeError 발생)
person.gender = 'Male'; // 무시됨 (strict mode에서는 TypeError 발생)
person.name = 'Mike'; // 무시됨 (strict mode에서는 TypeError 발생)
console.log(person); // { name: 'John', age: 30 }
console.log(Object.isFrozen(person)); // true
4. 불변객체(Immutable Object)
불변객체(Immutable Object)는 객체 생성 후 객체의 상태를 변경할 수 없는 객체로 이러한 객체는 생성 이후에는 추가, 삭제, 수정이 불가능하며, 객체 내부의 프로퍼티들도 불변한다. 대신, 객체를 변경하는 대신에 새로운 객체를 생성하거나 복사해서 원하는 상태를 만들 수 있다.
const obj = {
name: 'John',
age: 30,
address: {
street: '123 Main St',
city: 'New York',
state: 'NY'
}
};
function deepFreeze(obj) {
// 객체 동결
Object.freeze(obj);
// 모든 프로퍼티를 순회하며 중첩된 객체를 동결
Object.getOwnPropertyNames(obj).forEach(function(prop) {
const propValue = obj[prop];
if (typeof propValue === 'object' && propValue !== null) {
deepFreeze(propValue);
}
});
return obj;
}
const frozenObj = deepFreeze(obj);