--- title: Object.defineProperty() slug: Web/JavaScript/Reference/Global_Objects/Object/defineProperty tags: - ECMAScript 5 - JavaScript - Method - Object - Reference translation_of: Web/JavaScript/Reference/Global_Objects/Object/defineProperty ---
Object.defineProperty()
정적 메서드는 객체에 직접 새로운 속성을 정의하거나 이미 존재하는 속성을 수정한 후, 그 객체를 반환합니다.
참고: defineProperty
는 {{jsxref("Object")}} 인스턴스가 아니라 생성자에서 바로 호출해야 합니다.
Object.defineProperty(obj, prop, descriptor)
obj
prop
descriptor
주어진 대상 obj
.
defineProperty
는 객체의 속성을 정교하게 추가하거나 수정할 수 있습니다. 할당을 통해 속성을 추가하는 일반적인 방법을 사용하면 속성 열거({{jsxref("Statements/for...in", "for...in")}} 반복문이나 {{jsxref("Object.keys")}} 메서드)를 통해 노출되어 값을 변경하거나 {{jsxref("Operators/delete", "delete")}} 연산자로 삭제할 수 있습니다. defineProperty
를 사용하면 이런 부분을 상세하게 조절할 수 있습니다. Object.defineProperty()
로 추가한 속성은 기본적으로 불변합니다.
속성 서술자는 객체로 나타내며 데이터 서술자(data descriptors)와 접근자 서술자(accessor descriptors)의 두 가지 유형을 갖습니다. 데이터 서술자는 값을 가지는 속성으로, 덮어쓰거나 쓸 수 없습니다. 접근자 서술자는 접근자(getter)-설정자(setter) 한 쌍을 가지는 속성입니다. 서술자는 두 유형 중 하나여야 하며, 동시에 두 유형일 수는 없습니다.
데이터 서술자와 접근자 서술자 모두 객체이며 다음과 같은 키를 공유합니다.
configurable
true
.false
.enumerable
true
.false
.데이터 서술자는 다음 키를 선택사항으로 가집니다.
value
writable
true
.false
.접근자 서술자는 다음 키를 선택사항으로 가집니다.
get
this
값은 이 속성을 가진 객체(상속으로 인해 원래 정의한 객체가 아닐 수 있음)입니다.set
this
값은 이 속성을 가진 객체입니다.서술자가 value
, writable
, get
, set
키를 모두 지니고 있지 않으면 데이터 서술자로 간주합니다. 반면 value
또는 writable
과 동시에 get
또는 set
키를 함께 가지고 있으면 오류가 발생합니다.
각 설정값이 서술자 스스로의 속성일 필요는 없습니다. 따라서 서술자가 상속받은 값도 영향을 줍니다. 기본 설정값을 확실하게 보존하려면 {{jsxref("Object.prototype")}}을 먼저 동결하거나, 모든 속성을 명시적으로 지정하거나, {{jsxref("Object.create", "Object.create(null)")}}로 {{jsxref("null")}}을 가리키세요.
// __proto__ 사용 var obj = {}; var descriptor = Object.create(null); // 상속받은 속성 없음 // 기본으로 열거 불가, 설정 불가, 변경 불가 descriptor.value = 'static'; Object.defineProperty(obj, 'key', descriptor); // 명시적 Object.defineProperty(obj, 'key', { enumerable: false, configurable: false, writable: false, value: 'static' }); // 같은 객체를 재활용하기 function withValue(value) { var d = withValue.d || ( withValue.d = { enumerable: false, writable: false, configurable: false, value: null } ); d.value = value; return d; } Object.defineProperty(obj, 'key', withValue('static')); // Object.freeze가 존재하면 // 속성의 추가/제거 방지 // (value, get, set, enumerable, writable, configurable) (Object.freeze || Object)(Object.prototype);
이진 플래그 형태로 defineProperty
를 사용하는 예제는 additional examples에 있습니다.
Object.defineProperty()
는 지정한 속성이 객체에 존재하지 않으면 주어진 서술자를 사용해 새로 생성합니다. 서술자의 일부 항목은 생략 가능하며, 생략한 항목은 기본값을 대신 사용합니다.
var o = {}; // 새로운 객체 생성 // 데이터 속성 서술자와 defineProperty로 // 새로운 속성을 추가하는 예시 Object.defineProperty(o, 'a', { value: 37, writable: true, enumerable: true, configurable: true }); // 'a' 속성이 o 객체에 존재하고 값은 37 // 접근자 속성 기술자와 defineProperty로 // 새로운 속성을 추가하는 예시 var bValue = 38; Object.defineProperty(o, 'b', { // ES2015 단축 메서드명 사용 // 아래 코드와 같음 // get: function() { return bValue; } // set: function(newValue) { bValue = newValue; }, get() { return bValue; }, set(newValue) { bValue = newValue; }, enumerable: true, configurable: true }); o.b; // 38 // 'b' 속성이 o 객체에 존재하고 값은 38 // o.b를 재정의하지 않는 이상 // o.b의 값은 항상 bValue와 동일함 // 두 가지를 혼용할 수 없음 Object.defineProperty(o, 'conflict', { value: 0x9f91102, get: function() { return 0xdeadbeef; } }); // TypeError 발생 // value는 데이터 서술자에만, // get은 접근자 서술자에만 나타날 수 있음
ObjectdefineProperty()
는 지정한 속성이 객체에 이미 존재하면 주어진 서술자와 객체의 기존 설정을 사용해 속성의 수정을 시도합니다. 기존 속성 서술자의 configurable
이 false
이면 속성이 "설정 불가능"하다고 말하고, 이 속성의 어떤 특성도 수정할 수 없습니다. 다만 쓰기 가능한 데이터 속성의 경우 값을 바꾸거나 writable
특성을 true
에서 false
로 바꾸는건 가능합니다. 속성이 설정 불가능한 경우 속성의 유형을 데이터에서 접근자, 또는 접근자에서 데이터로 바꿀 수 없습니다.
설정 불가능한 속성의 특성을 바꾸려고 시도하면 {{jsxref("TypeError")}}가 발생합니다. 단, 기존 값과 신규 값이 같은 경우, 혹은 쓰기 가능한 속성의 value
와 writable
은 수정할 수 있습니다.
writable
특성속성의 writable
특성이 false
인 경우는 "쓰기 불가능"하여 다시 할당할 수 없습니다.
var o = {}; // 새로운 객체 생성 Object.defineProperty(o, 'a', { value: 37, writable: false }); console.log(o.a); // 37 기록 o.a = 25; // 오류 발생하지 않음 // 엄격 모드에서는 값이 같더라도 // 오류가 발생함 console.log(o.a); // 37 기록, 할당하지 못했음 // 엄격 모드 (function() { 'use strict'; var o = {}; Object.defineProperty(o, 'b', { value: 2, writable: false }); o.b = 3; // TypeError: "b" is read-only return o.b; // 윗줄이 없다면 2 반환 }());
위의 예제가 보이듯, 비엄격 모드에서는 쓰기 불가능한 속성의 값에 쓰려고 시도하면 값이 바뀌지 않고, 오류도 발생하지도 않습니다.
enumerable
은 해당 속성이 {{jsxref("Statements/for...in", "for...in")}} 루프나 {{jsxref("Object.keys()")}} 에서 노출될지 말지를 정의한다.
var o = {}; Object.defineProperty(o, 'a', { value: 1, enumerable: true }); Object.defineProperty(o, 'b', { value: 2, enumerable: false }); Object.defineProperty(o, 'c', { value: 3 }); // enumerable defaults to false o.d = 4; // enumerable defaults to true // when creating a property by setting it Object.defineProperty(o, Symbol.for('e'), { value: 5, enumerable: true }); Object.defineProperty(o, Symbol.for('f'), { value: 6, enumerable: false }); for (var i in o) { console.log(i); } // logs 'a' and 'd' (in undefined order) Object.keys(o); // ['a', 'd'] o.propertyIsEnumerable('a'); // true o.propertyIsEnumerable('b'); // false o.propertyIsEnumerable('c'); // false o.propertyIsEnumerable('d'); // true o.propertyIsEnumerable(Symbol.for('e')); // true o.propertyIsEnumerable(Symbol.for('f')); // false var p = { ...o } p.a // 1 p.b // undefined p.c // undefined p.d // 4 p[Symbol.for('e')] // 5 p[Symbol.for('f')] // undefined
configurable
은 객체에서 해당키가 제거될 수 있는지와 (writable
을 제외한)기술속성을 변경할 수 있는지에 대한 여부를 동시에 통제한다.
var o = {}; Object.defineProperty(o, 'a', { get() { return 1; }, configurable: false }); Object.defineProperty(o, 'a', { configurable: true }); // throws a TypeError Object.defineProperty(o, 'a', { enumerable: true }); // throws a TypeError Object.defineProperty(o, 'a', { set() {} }); // throws a TypeError (set was undefined previously) Object.defineProperty(o, 'a', { get() { return 1; } }); // throws a TypeError // (even though the new get does exactly the same thing) Object.defineProperty(o, 'a', { value: 12 }); // throws a TypeError console.log(o.a); // logs 1 delete o.a; // Nothing happens console.log(o.a); // logs 1
o.a의 configurable 가 true라면, 위의 예외는 발생하지 않고 속성은 마지막에 제거되었을 것이다.
속성을 정의할 때 기본값을 제공하는 방식은 중요하다. 간단히 점구문을 이용해 할당한 값과 Object.defineProperty
를 사용한 경우는 꽤 다르다. 아래 예를 보자.
var o = {}; o.a = 1; // 위의 표현은 아래와 같다: Object.defineProperty(o, 'a', { value: 1, writable: true, configurable: true, enumerable: true }); // 만약 다음과 같이 표현한다면, Object.defineProperty(o, 'a', { value: 1 }); // 아래의 의미를 지니게 된다: Object.defineProperty(o, 'a', { value: 1, writable: false, configurable: false, enumerable: false });
아래의 예는 어떻게 스스로 변화를 기록해두는 객체를 만드는지 보여준다. temperature
속성의 값을 바꾸면 archive
배열에도 로그가 쌓인다.
function Archiver() { var temperature = null; var archive = []; Object.defineProperty(this, 'temperature', { get: function() { console.log('get!'); return temperature; }, set: function(value) { temperature = value; archive.push({ val: temperature }); } }); this.getArchive = function() { return archive; }; } var arc = new Archiver(); arc.temperature; // 'get!' arc.temperature = 11; arc.temperature = 13; arc.getArchive(); // [{ val: 11 }, { val: 13 }]
Specification | Status | Comment |
---|---|---|
{{SpecName('ES5.1', '#sec-15.2.3.6', 'Object.defineProperty')}} | {{Spec2('ES5.1')}} | Initial definition. Implemented in JavaScript 1.8.5. |
{{SpecName('ES6', '#sec-object.defineproperty', 'Object.defineProperty')}} | {{Spec2('ES6')}} | |
{{SpecName('ESDraft', '#sec-object.defineproperty', 'Object.defineProperty')}} | {{Spec2('ESDraft')}} |
{{Compat("javascript.builtins.Object.defineProperty")}}
length
property of an Array
objectIt is possible to redefine the {{jsxref("Array.length", "length")}} property of arrays, subject to the usual redefinition restrictions. (The {{jsxref("Array.length", "length")}} property is initially non-configurable, non-enumerable, and writable. Thus on an unaltered array it is possible to change the {{jsxref("Array.length", "length")}} property's value, or to make it non-writable. It is not allowed to change its enumerability or configurability, or if it is non-writable to change its value or writability.) However, not all browsers permit this redefinition.
Firefox 4 through 22 will throw a {{jsxref("Global_Objects/TypeError", "TypeError")}} on any attempt whatsoever (whether permitted or not) to redefine the {{jsxref("Array.length", "length")}} property of an array.
Versions of Chrome which implement Object.defineProperty()
in some circumstances ignore a length value different from the array's current {{jsxref("Array.length", "length")}} property. In some circumstances changing writability seems to silently not work (and not throw an exception). Also, relatedly, some array-mutating methods like {{jsxref("Array.prototype.push")}} don't respect a non-writable length.
Versions of Safari which implement Object.defineProperty()
ignore a length
value different from the array's current {{jsxref("Array.length", "length")}} property, and attempts to change writability execute without error but do not actually change the property's writability.
Only Internet Explorer 9 and later, and Firefox 23 and later, appear to fully and correctly implement redefinition of the {{jsxref("Array.length", "length")}} property of arrays. For now, don't rely on redefining the {{jsxref("Array.length", "length")}} property of an array to either work, or to work in a particular manner. And even when you can rely on it, there's really no good reason to do so.
Internet Explorer 8 implemented a Object.defineProperty()
method that could only be used on DOM objects. A few things need to be noted:
Object.defineProperty()
on native objects throws an error.Configurable
, enumerable
and writable
attributes should all be set to true
for data descriptor and true
for configurable
, false
for enumerable
for accessor descriptor.(?) Any attempt to provide other value(?) will result in an error being thrown.Object.defineProperty
examples