자바스크립트 엔진은 프로퍼티를 생성할 때 프로퍼티의 상태를 나타내는 프로퍼티 어트리뷰트를 기본값으로 자동 정의
프로퍼티 상태 : 프로퍼티 값(value), 값의 갱신 가능 여부(writable), 열거 가능 여부(enumerable), 재정의 가능 여부(configurable)
const person = {
name: 'Lee'
};
person.age = 20;
// 프로퍼티 어트리뷰트 정보를 제공하는 프로퍼티 디스크럽터 객체 반환
console.log(Object.getOwnPropertyDescriptor(person, 'name'));
// name : {value: "Lee", writable: true, enumerable: true, configurable: true}
// age : {value: 20, writable: true, enumerable: true, configurable: true}
프로퍼티 디스크립터 객체의 프로퍼티
프로퍼티 디스크립터 객체의 프로퍼티
const person = {
// 데이터 프로퍼티
fristName: 'Ungmo',
laseName: 'Lee',
// fullName은 접근자 함수로 구성된 접근자 프로퍼티
// getter 함수
get fullName() {
return `${this.firstName} ${this.lastName}`;
},
//setter 함수
set fullName(name) {
[this.firstName, this.lastName] = name.split(' ');
}
};
// 데이터 프로퍼티를 통한 프로퍼티 값 참조
console.log(person.firstName + ' ' + person.lastName); // Ungmo Lee
// 접근자 프로퍼티 funllName에 값을 저장하면 setter 함수가 호출
person.fullName = 'Heegun Lee';
console.log(person); // {firstName: "Heegun", lastName: "Lee"}
💡 프로토타입 (prototype)
- 어떤 객체의 상위(부모) 객체의 역활을 하는 객체
- 프로토타입은 하위(자식) 객체에게 자신의 프로퍼티와 메서드를 상속함
// 일반 객체의 __proto__는 접근자 프로퍼티
Object.getOwnPropertyDescriptor(Object.prototype, '__proto__');
// 함수 객체의 prototype은 데이터 프로퍼티
Object.getOwnPropertyDescriptor(function() {}, 'prototype');
새로운 프로퍼티를 추가하면서 프로퍼티 어트리뷰트를 명시적으로 정의하거나, 기존 프로퍼티의 프로퍼티 어트리뷰트를 재정의하는 것
Object.defineProperty 메서드를 통해 프로퍼티의 어트리뷰트를 정의할 수 있음
Object.defineProperty 메서드로 프로퍼티를 정의할 때 프로퍼티 디스크립터 객체의 프로퍼티를 일부 생략 가능
const person ={};
Object.defineProperties(person,{
//데이터 프로퍼티
firstName:{
value : 'Jayce',
writable : true,
enumerable : true,
configurable : true
},
lastName:{
value : 'Yu',
writable : true,
enumerable : true,
configurable : true
},
//접근자 프로퍼티
fullName : {
get(){
return `$[this.firstName] $[this.lastName]`;
};
set(name){
[this.firstName, this.lastName] = name.split(' ');
},
enumerable : true,
configurable : true
}
});
const person = { name: 'Lee' };
console.log(Object.isExtensible(person)); // true
Object.preventExtensions(person); // 프로퍼티 추가 금지
console.log(Object.isExtensible(person)); // false
person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
delete person.name;
console.log(person); // {}
// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'age', { value: 20 }); // TypeError
const person = { name: 'Lee' };
console.log(Object.isSealed(person)); // false
Object.seal(person); // 객체 밀봉하여 프로퍼티 추가, 삭제, 재정의 금지
console.log(Object.isSealed(person)); // true
// 밀봉된 객체는 configurable -> false
console.log(Object.getOwnPropertyDescriptors(person));
// name: {value: "Lee", writable: true, enumerable: true, configurable: false}
person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
delete person.name; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'age', { value: 20 }); // TypeError
const person = { name: 'Lee' };
console.log(Object.isFrozen(person)); // false
Object.freeze(person); // 객체 동결 프로퍼티 추가, 삭제, 재정의, 쓰기 금지
console.log(Object.isFrozen(person)); // true
// 밀봉된 객체는 configurable -> false
console.log(Object.getOwnPropertyDescriptors(person));
// name: {value: "Lee", writable: true, enumerable: true, configurable: false}
person.age = 20; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
delete person.name; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
person.name = 'Kim'; // 무시 strict mode에서는 에러
console.log(person); // {name: "Lee"}
// 프로퍼티 정의에 의한 프로퍼티 추가도 금지
Object.defineProperty(person, 'name', { configuralbe: true }); // TypeError
const person = {
name: 'Lee',
address: { city: 'Seoul' }
};
// 얕은 객체 동결
Object.freeze(person);
// 직속 프로퍼티만 동결
console.log(Object.isFrozen(person)); // true
// 중첩 객체까지 동결 불가능
console.log(Object.isFrozen(person.address)); // false
person.address.city = 'Busan';
console.log(person); // {name: "Lee", address: {city: "Busan"}}
객체 중첩 객체까지 동결하여 변경이 불가능한 읽기 전용의 불변 객체 구현
function deepFreeze(target) {
if (target && typeof target === 'object' && !Object.isFrozen(target)) {
Object.freeze(target);
Object.keys(target).forEach(key => deepFreeze(target[key]));
}
return target;
}
const person = {
name: 'Lee',
address: { city: 'Seoul' }
};
// 얕은 객체 동결
deepFreeze(person);
// 직속 프로퍼티만 동결
console.log(Object.isFrozen(person)); // true
// 중첩 객체까지 동결
console.log(Object.isFrozen(person.address)); // true
person.address.city = 'Busan';
console.log(person); // {name: "Lee", address: {city: "Seoul"}}
📖 참고도서 : 모던 자바스크립트 Deep Dive 자바스크립트의 기본 개념과 동작 원리 / 이웅모 저 | 위키북스