현재 번역은 완벽하지 않습니다. 한국어로 문서 번역에 동참해주세요.

Object.defineProperty() 메소드는 객체에 직접 새로운 속성을 정의하거나 이미 존재하는 객체를 수정한 뒤 그 객체를 반환한다.

Syntax

Object.defineProperty(obj, prop, descriptor)

Parameters

obj
속성을 정의하고자 하는 객체.
prop
새로 정의하거나 수정하려는 속성의 이름.
descriptor
새로 정의하거나 수정하려는 속성에 대해 기술하는 객체.

Description

이 메소드는 개체의 속성에 대해 정교한 추가나 수정을 가능케한다. 보통 속성을 추가하면 (for...in 를 통한 열거루프나 Object.keys method)메소드를 통해 노출되고, 값을 변경할 수 있으며 deleted연산자를 통해 삭제될 수 있다. 이 메소드를 이용하면 이러한 기본 작동을 뛰어넘어
자세한 설정을 할 수 있다. 기본적으로 Object.defineProperty() 를 이용해 추가한 값들은 수정불가능이다.

객체로 표현되는 속성 기술자(Property descriptors)는 두 가지 타입으로 되어있다: 데이터 기술(data descriptors) 또는 데이터 접근기술(accessor descriptors)이다. 데이터 기술에는 value속성이 있고 읽기전용인지 쓸 수 있는지를 나타내는 writable속성을 추가적으로 포함할 수도 있다. 데이터 접근기술은 getter-setter쌍의 함수로 기술된다. 속성기술자에서는 반드시 이 두 가지 타입 중 한 가지로 기술되어야한다; 동시에 두 가지 타입을 기술할 수는 없다.

데이터 기술과 데이터 접근기술은 객체다. 이들은 다음의 필수 키를 공유한다:

configurable
이 속성기술자는 해당 객체로부터 그 속성을 제거할 수 있는지를 기술한다. true 라면 삭제할 수 있다.
기본값은 false.
enumerable
해당 객체의 키가 열거 가능한지를 기술한다. true 라면 열거가능하다.
기본값은 false.

데이터 기술(data descriptor)은 다음의 키를 선택적으로 갖는다:

value
속성에 해당되는 값으로 오직 적합한 자바스크립트 값 (number, object, function, etc) 만 올 수 있다.
기본값은 undefined.
writable
writable이 true로 설정되면 할당연산자assignment operator를 통해 값을 바꿀 수 있다.
기본값은 false.

데이터 접근기술(accessor descriptor)은 다음의 키를 선택적으로 갖는다:

get
속성의 값을 얻는 목적으로 사용되는 getter용 함수로서 만약 getter를 제공하지 않는다면 undefined가 온다. 이 함수의 반환값은 속성의 값으로 사용된다.
기본값은 undefined.
set
속성의 값을 설정하기 위한 setter용 함수로서 setter를 제공하지 않는다면 undefined 가 온다. 이 함수는 오직 하나의 인자를 받으며 해당 속성의 값으로 할당한다..
기본값은 undefined.

이러한 옵션들이 반드시 객체 자신의 속성일 필요는 없기 때문에 상속되는 경우도 고려해야한다. 이러한 기본값들에 대해 보장하고 싶다면, 우선 모든 옵션을 명시적으로 정의하거나, __proto__ 속성이 null을 가리키게 한 뒤 Object.prototype을 얼려야(freeze)한다.

// __proto__ 를 사용하기
var obj = {};
Object.defineProperty(obj, 'key', {
  __proto__: null, // 상속되는 속성이 없음
  value: 'static'  // enumerable 불가
                   // configurable 불가
                   // writable 불가
                   // 위의 항목이 기본값으로 설정됨
});

// 명시적으로 정의하기
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'));

// 얼리기(freeze)가 가능하다면 객체의 prototype에 속성을
// 추가하거나 제거하지 못하게 막는다.
// (value, get, set, enumerable, writable, configurable)  
(Object.freeze || Object)(Object.prototype);

Examples

바이너리플래그 형식으로 Object.defineProperty 메소드를 사용하는 예를 보고 싶다면  additional examples를 참고한다.

속성 생성하기

그 속성이 객체에 존재하지 않는 경우, Object.defineProperty() 는 기술된 대로 새 속성을 만든다. 기술자에 생략된 필드들은 기본값들로 대체된다. 린값으로 정의되는 모든 필드들은 기본적으로 false 가 된다. value, get, set 필드의 기본값은 undefined 다. A property which is defined without get/set/value/writable 없이 정의된 속성은 “generic” 이라 하며 데이터기술(data descriptor)이 적용된 것 본다(즉 value/writable 이 생략된 것으로 본다)

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', {
  get: function() { return bValue; },
  set: function(newValue) { bValue = newValue; },
  enumerable: true,
  configurable: true
});
o.b; // 38
// 'b' 속성이 o 객체에 존재하고 값은 38이다
// 재정의하지 않는 이상 o.b의 값은 항상 bValue와 동일하다

// 두 가지를 섞어서 정의할 수 없다:
Object.defineProperty(o, 'conflict', {
  value: 0x9f91102,
  get: function() { return 0xdeadbeef; }
});
// TypeError예외가 발생한다: value키는 데이터기술에서만 나타날 수 있고, get키는 데이터 접근기술에서만 나타날 수 있다.

속성 수정하기

Object.defineProperty()는 이미 존재하는 속성의 경우 속성기술자의 값과 객체의 현재 설정에 있는 값을 이용해 해당 속성을 수정하려 시도한다. 이전 속성기술자에 configurable 속성이 false 라면(이러한 속성을 "non-configurable"이라 한다), writable외엔 어떤 속성도 바뀌지 않을 것이다. 이 경우 데이터기술과 접근기술사이에 교체하는 것도 불가능하다.

속성이 non-configurable인 경우 writable 속성은 오직 false로만 바뀔 수 있다.

non-confirable속성에 (writable 을 제외한) 기존과 다른 값으로 기술속성을 바꾸려하면 TypeError 예외가 발생한다.

Writable 속성

writablefalse인 경우 해당 속성은 “non-writable” 이라 한다. 이 경우 해당 속성의 값을 재할당할 수 없다.

var o = {}; // 새로운 객체 생성

Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});

console.log(o.a); // logs 37
o.a = 25; // 예외는 발생하지 않음 (strict모드에서는 같은 값을 할당한다 할지라도 예외가 발생할 것이다)
console.log(o.a); // logs 37. 할당되지 않았음.

위의 예에서 non-writable 속성에 값을 쓰려는 시도는 반영되지 않으며 예외도 발생하지 않는다..

Enumerable 속성

enumerable 은 해당 속성이 for...in 루프나 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의 기본값은 false
o.d = 4; // 기존 방식으로 키를 설정하여 생성하는 경우, enumerable의 기본값은 true

for (var i in o) {
  console.log(i);
}
// 'a', 'd' 출력 (순서는 무작위)

Object.keys(o); // ['a', 'd']

o.propertyIsEnumerable('a'); // true
o.propertyIsEnumerable('b'); // false
o.propertyIsEnumerable('c'); // false

Configurable 속성

configurable 은 객체에서 해당키가 제거될 수 있는지와 (writable을 제외한)기술속성을 변경할 수 있는지에 대한 여부를 동시에 통제한다.

var o = {};
Object.defineProperty(o, 'a', {
  get: function() { 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: function() {} }); // throws a TypeError (set은 이전에 undefined였음)
Object.defineProperty(o, 'a', { get: function() { return 1; } }); // throws a TypeError (새로운 get이 완전히 동일하다 할지라도 예외발생)
Object.defineProperty(o, 'a', { value: 12 }); // throws a TypeError

console.log(o.a); // logs 1
delete o.a; // 아무일도 안일어남
console.log(o.a); // logs 1

o.aconfigurabletrue라면, 위의 예외는 발생하지 않고 속성은 마지막에 제거되었을 것이다.

속성에 기본값 추가하기

속성을 정의할 때 기본값을 제공하는 방식은 중요하다. 간단히 점구문을 이용해 할당한 값과 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
});

사용자 정의 Setters 와 Getters

아래의 예는 어떻게 스스로 변화를 기록해두는 객체를 만드는지 보여준다. 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 }]

Specifications

Specification Status Comment
ECMAScript 5.1 (ECMA-262)
The definition of 'Object.defineProperty' in that specification.
Standard Initial definition. Implemented in JavaScript 1.8.5.
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Object.defineProperty' in that specification.
Standard  

Browser compatibility

Feature Firefox (Gecko) Chrome Internet Explorer Opera Safari
Basic support 4.0 (2) 5 (previous versions untested) 9 (8, but only on DOM objects and with some non-standard behaviors. See below.) 11.60 5.1 (5, but not on DOM objects)
Feature Firefox Mobile (Gecko) Android IE Mobile Opera Mobile Safari Mobile
Basic support 4.0 (2) (Yes) 9 and above 11.50 (Yes)

Based on Kangax's compat tables.

Redefining the length property of an Array object

It is possible to redefine the length property of arrays, subject to the usual redefinition restrictions. (The length property is initially non-configurable, non-enumerable, and writable. Thus on an unaltered array it is possible to change the 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 TypeError on any attempt whatsoever (whether permitted or not) to redefine the 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 length property. In some circumstances changing writability seems to silently not work (and not throw an exception). Also, relatedly, some array-mutating methods like 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 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 length property of arrays. For now, don't rely on redefining the 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 specific notes

Internet Explorer 8 implemented a Object.defineProperty() method that could only be used on DOM objects. A few things need to be noted:

  • Trying to use Object.defineProperty() on native objects throws an error.
  • Property attributes must be set to some values. 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.
  • Reconfiguring a property requires first deleting the property. If the property isn't deleted, it stays as it was before the reconfiguration attempt.

See also

문서 태그 및 공헌자

 이 페이지의 공헌자: mixed, bsidesoft
 최종 변경: mixed,