--- 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 ---
{{JSRef}}

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

참고: defineProperty는 {{jsxref("Object")}} 인스턴스가 아니라 생성자에서 바로 호출해야 합니다.

{{EmbedInteractiveExample("pages/js/object-defineproperty.html")}}

구문

Object.defineProperty(obj, prop, descriptor)

매개변수

obj
속성을 정의할 객체.
prop
새로 정의하거나 수정하려는 속성의 이름 또는 {{jsxref("Symbol")}}.
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
속성에 연관된 값. 아무 유효한 JavaScript 값(숫자, 객체, 함수 등)이나 가능합니다.
기본값은 {{jsxref("undefined")}}
writable
{{jsxref("Operators/Assignment_Operators", "할당 연산자", "", 1)}}로 속성의 값을 바꿀 수 있다면 true.
기본값은 false.

접근자 서술자는 다음 키를 선택사항으로 가집니다.

get
속성 접근자로 사용할 함수, 접근자가 없다면 {{jsxref("undefined")}}. 속성에 접근하면 이 함수를 매개변수 없이 호출하고, 그 반환값이 속성의 값이 됩니다. 이 때 this 값은 이 속성을 가진 객체(상속으로 인해 원래 정의한 객체가 아닐 수 있음)입니다.
기본값은 {{jsxref("undefined")}}.
set
속성 설정자로 사용할 함수, 설정자가 없다면 {{jsxref("undefined")}}. 속성에 값을 할당하면 이 함수를 하나의 매개변수(할당하려는 값)로 호출합니다. 이 때 this 값은 이 속성을 가진 객체입니다.
기본값은 {{jsxref("undefined")}}.

서술자가 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()는 지정한 속성이 객체에 이미 존재하면 주어진 서술자와 객체의 기존 설정을 사용해 속성의 수정을 시도합니다. 기존 속성 서술자의 configurablefalse이면 속성이 "설정 불가능"하다고 말하고, 이 속성의 어떤 특성도 수정할 수 없습니다. 다만 쓰기 가능한 데이터 속성의 경우 값을 바꾸거나 writable 특성을 true에서 false로 바꾸는건 가능합니다. 속성이 설정 불가능한 경우 속성의 유형을 데이터에서 접근자, 또는 접근자에서 데이터로 바꿀 수 없습니다.

설정 불가능한 속성의 특성을 바꾸려고 시도하면 {{jsxref("TypeError")}}가 발생합니다. 단, 기존 값과 신규 값이 같은 경우, 혹은 쓰기 가능한 속성의 valuewritable은 수정할 수 있습니다.

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 속성

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 속성

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
});

사용자 정의 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 }]

명세

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")}}

호환성 참고사항

Redefining the length property of an Array object

It 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 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:

같이 보기