JavaScript에서 Object.defineProperty() 메서드는 객체에 새로운 속성을 직접 정의하거나 기존 속성을 수정하는 데 사용됩니다. 이 메서드는 수정된 객체 자체를 반환합니다.
const myObject = {};
Object.defineProperty(myObject, 'immutableValue', {
value: 42,
writable: false // 이 속성은 변경할 수 없습니다.
});
// strict mode에서는 오류 발생
// myObject.immutableValue = 77;
console.log(myObject.immutableValue); // 42
메서드 매개변수
obj: 속성을 정의하거나 수정할 대상 객체입니다.prop: 정의하거나 수정할 속성의 이름 (문자열 또는 Symbol) 입니다.descriptor: 속성을 정의하거나 수정하기 위한 속성 설명자 객체입니다.
Object.defineProperty(targetObject, 'dataProperty', {
value: 100,
writable: false, // 값 변경 가능 여부
configurable: false, // 속성 삭제 및 재정의 가능 여부
enumerable: true, // for...in 루프 또는 Object.keys()에 포함 여부
get: undefined, // 속성에 접근 시 호출될 getter 함수
set: undefined // 속성 값이 변경될 때 호출될 setter 함수
});
기본값
속성을 정의할 때 writable, configurable, enumerable 속성을 명시하지 않으면 기본값은 다음과 같습니다.
value를 지정하면, 다른 속성이 명시되지 않은 경우writable,configurable,enumerable은 모두false가 됩니다.value외의 속성(getter, setter 등)을 지정하면,writable과enumerable은false가 되고configurable은false가 됩니다.
let sampleObj = {};
// 간단한 할당 (기본값: writable=true, configurable=true, enumerable=true)
sampleObj.propA = 1;
// 위 코드는 아래와 유사하게 동작합니다:
// Object.defineProperty(sampleObj, "propA", {
// value: 1,
// writable: true,
// configurable: true,
// enumerable: true
// });
// Object.defineProperty를 직접 사용할 때 (기본값: writable=false, configurable=false, enumerable=false)
Object.defineProperty(sampleObj, "propB", { value: 2 });
// 위 코드는 아래와 유사하게 동작합니다:
// Object.defineProperty(sampleObj, "propB", {
// value: 2,
// writable: false,
// configurable: false,
// enumerable: false
// });
사용자 정의 Getter 및 Setter
get과 set 접근자를 사용하여 속성 접근 및 할당 시 사용자 정의 로직을 실행할 수 있습니다.
function TemperatureMonitor() {
let currentTemp = null;
const history = [];
Object.defineProperty(this, 'temperature', {
get: function() {
console.log('온도 조회 시도');
return currentTemp;
},
set: function(newValue) {
console.log(`온도 변경 시도: ${newValue}`);
currentTemp = newValue;
history.push({ recordedValue: currentTemp });
}
});
this.getHistory = function() { return history; };
}
const monitor = new TemperatureMonitor();
console.log(monitor.temperature); // "온도 조회 시도" 출력 후 undefined 반환
monitor.temperature = 25; // "온도 변경 시도: 25" 출력
monitor.temperature = 28; // "온도 변경 시도: 28" 출력
console.log(monitor.getHistory()); // [{ recordedValue: 25 }, { recordedValue: 28 }] 출력
다음 예제는 getter가 항상 고정된 값을 반환하고, setter는 다른 속성을 설정하는 경우입니다.
const predefinedBehavior = {
get: function() {
return '이 문자열은 항상 반환됩니다.';
},
set: function(assignedValue) {
// 할당된 값은 무시하고, 대신 다른 속성을 설정합니다.
this.internalName = '내부 이름 설정됨';
}
};
function ExampleWithGettersSetters() {
Object.defineProperty(this, 'specialProperty', predefinedBehavior);
}
const instance = new ExampleWithGettersSetters();
instance.specialProperty = '임의의 값'; // setter 호출됨
console.log(instance.specialProperty); // "이 문자열은 항상 반환됩니다." 출력
console.log(instance.internalName); // "내부 이름 설정됨" 출력
프로토타입에서의 속성 공유
get과 set 접근자를 프로토타입에 정의하면, 해당 생성자로 생성된 모든 인스턴스가 해당 속성을 공유하게 됩니다.
function SharedCounter() {}
let sharedValue;
Object.defineProperty(SharedCounter.prototype, "count", {
get() {
return sharedValue;
},
set(newValue) {
sharedValue = newValue;
}
});
const counterA = new SharedCounter();
const counterB = new SharedCounter();
counterA.count = 10;
console.log(counterB.count); // 10
Object.defineProperties()
Object.defineProperties() 메서드는 Object.defineProperty()와 유사하지만, 여러 속성을 한 번에 정의하거나 수정할 수 있다는 장점이 있습니다.
const multiPropObject = {};
Object.defineProperties(multiPropObject, {
'readOnlyProp': {
value: 'Immutable',
writable: false,
enumerable: true
},
'configurableProp': {
value: 123,
configurable: true
}
// ... 추가 속성 정의 가능
});
console.log(multiPropObject.readOnlyProp); // Immutable
console.log(multiPropObject.configurableProp); // 123