출처 모던 자바스크립트 Deep Dive을 보고 정리한 내용입니다.
프로퍼티 어트리뷰트란 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내기 위해 자바스크립트 엔진이 생성하는 값이다.
const obj = {
name: 'JKCho'
};
console.log(Object.getOwnPropertyDescriptor(obj, 'name'));
//{value: "JKCho", writable: true, enumerable: true, configurable: true}
obj.age = 26;
console.log(Object.getOwnPropertyDescriptors(obj));
//{
// name: {value: "JKCho", writable: true, enumerable: true, configurable: true},
// age: {value: 26, writable: true, enumerable: true, configurable: true}
//}
Object.getOwnPropertyDescriptor 함수를 통해 접근할 수 있으며 value, writable, enumerable, configurable를 가진 프로퍼티 디스크럽터 객체가 반환된다. value는 값은 객체의 value값과 동일하고, 나머지 값은 기본적으로 true로 설정되어 있다.
Object.getOwnPropertyDescriptors함수는 객체의 모든 프로퍼티의 프로퍼티 디스크럽터를 반환한다.
const obj = {};
//프로퍼티 어트리뷰트 정의
Object.defineProperty(obj, 'firstName', {
value: 'JK',
writable: true,
enumerable: true,
configurable: true
});
console.log(Object.getOwnPropertyDescriptor(obj, 'firstName'));
//{value: "JK", writable: true, enumerable: true, configurable: true}
Object.defineProperty(obj, 'lastName', {
value: 'Cho',
});
console.log(Object.getOwnPropertyDescriptor(obj, 'lastName'));
//{value: "Cho", writable: false, enumerable: false, configurable: false}
//프로퍼티를 누락시키면 undefined 또는 false로 할당된다.
obj.lastName = 'Kim' //writable이 false인 경우 동작하지 않는다. 에러는 발생하지 않고 무시된다.
console.log(Object.keys(obj)); //['firstName']
//enumerable이 false인 경우 for...in문과 함께 열거되지 않는다
delete obj.lastName; //configurable이 false인 경우 무시된다. writable이 true인 경우 value를 변경하거나 writable의 값을 false로 변경하는 것은 가능하다.
const obj = {};
Object.defineProperties(obj, {
firstName: {
value: 'JK',
writable: true,
enumerable: true,
configurable: true
},
lastName: {
value: 'Cho',
writable: true,
enumerable: true,
configurable: true
},
fullName: {
get() { //getter 함수
return`${this.firstName, this.lastNAme}`;
},
set(name) { //setter함수
[this.firstName, this.lastName] = name.split(' ');
}
}
});
const descriptor = Object.getOwnPropertyDescriptor(obj, 'fullName');
console.log(descriptor); //fullName {get: f, set: f, enumerable: true, configurable: true}
obj.fullName('Peter Park');
console.log(obj); //firstName: 'Peter', lastName: 'Park'
const obj = {name: 'JKCho'};
console.log(obj.isExtensible); //true
Object.preventExtensions(true);
console.log(obj.isExtensible); //false
obj.age = 26; //무시, strict모드에서는 에러 발생
delete obj.name;
console.log(obj); //{}, 삭제는 가능
const obj = {name: 'JKCho'};
console.log(obj.isSealed); //false
Object.seal(true);
console.log(obj.isSealed); //true
console.log(Object.getOwnPropertyDescriptors(obj));
//{ name: {value: 'JKCho', writable: true, enumerable: true, configurable: false},}
obj.age = 26; //무시, strict모드에서는 에러 발생
delete obj.name; //무시, strict모드에서는 에러 발생
console.log(obj); //{name: 'JKCho'}
obj.name = 'PeterKim';
console.log(obj); //{name: 'PeterKim'}, 프로퍼티 값 갱신은 가능
Object.defineProperty(obj, 'name', {configurable: true});//TypeError, 프로퍼티 어트리뷰트는 재정의가 금지된다.
const obj = {name: 'JKCho'};
console.log(obj.isFrozen); //false
Object.freeze(true);
console.log(obj.isFrozen); //true
console.log(Object.getOwnPropertyDescriptors(obj));
//{ name: {value: 'JKCho', writable: false, enumerable: true, configurable: false},}
obj.age = 26; //무시, strict모드에서는 에러 발생
delete obj.name; //무시, strict모드에서는 에러 발생
console.log(obj); //{name: 'JKCho'}
obj.name = 'PeterKim';
console.log(obj); //{name: 'JK Cho'}, 무시, 프로퍼티 값 갱신이 불가능하다
Object.defineProperty(obj, 'name', {configurable: true});//TypeError, 프로퍼티 어트리뷰트는 재정의가 금지된다.
위와 같은 방식의 변경 방지는 얕은 변경 방지로 직속 프로퍼티만 변경되고 중첩 객체의 프로퍼티에는 적용되지 않는다.
const obj = {
name: 'JKCho'
location: {
city: '경기도',
state: '안양시'
}
};
Object.freeze(obj);
console.log(Object.isFrozen(obj)); //true
console.log(Object.isFrozen(obj.location)); //false
그러므로 다음과 같이 재귀함수를 이용해 중첩객체까지 변경방지를 적용할 수 있다.
const obj = {
name: 'JKCho'
location: {
city: '경기도',
state: '안양시'
}
};
function deepFreeze(target) {
if(target && typeof target === 'object' && !Object.isFrozen(target)){
Object.freeze(target);
Object.keys(target).forEach((key) => deepFreeze(target[key]));
}
return target;
}
deepFreeze(obj);
console.log(Object.isFrozen(obj)); //true
console.log(Object.isFrozen(obj.location)); //true