Object.defineProperty

JavaScript 1.8.5 で導入

概要

オブジェクトで新しいプロパティを直接定義、またはオブジェクトで既存のプロパティを変更して、そのオブジェクトを返します。

Object オブジェクトのメソッド
実装されたバージョン JavaScript 1.8.5
ECMAScript エディション ECMAScript 5th Edition

構文

Object.defineProperty(obj, prop, descriptor)

引数

obj
プロパティを定義するオブジェクトです。
prop
定義または変更するプロパティの名前です。
descriptor
定義または変更されるプロパティのディスクリプタです。

説明

このメソッドで、オブジェクト上でプロパティを明示的に追加または変更することができます。代入による通常のプロパティ追加では、プロパティ列挙 (for...in ループ) に現れ、値を変更可能で、また削除可能なプロパティを生成します。このメソッドでは、これらの詳細事項を既定値から変えることが可能です。

オブジェクトに存在するプロパティのディスクリプタは主に 2 種類あります: データディスクリプタと、アクセサディスクリプタです。データディスクリプタ は値を持つプロパティで、その値は書き換え可能または不可能にできます。アクセサディスクリプタ は、関数の getter と setter の組で表されるプロパティです。ディスクリプタはこれら 2 種類のいずれかでなければなりません。つまり、両方を兼ねることができません。ディスクリプタはどちらの種類でも、configurable フィールドおよび enumerable フィールドを含みます。

プロパティのディスクリプタは以下のフィールドを持つオブジェクトです。

configurable
true である場合、この種のディスクリプタを変更することや、対応するオブジェクトからプロパティを削除することができます。既定値は false です。
enumerable
true である場合、このプロパティは対応するオブジェクトでのプロパティ列挙に現れます。既定値は false です。

データディスクリプタは次のオプションキーを持つオブジェクトです。

value
プロパティに関連づけられた値です (データディスクリプタのみ)。既定値は undefined です。
writable
true である場合、プロパティに関連づけられた値は変更することができます (データディスクリプタのみ)。既定値は false です。

アクセサディスクリプタは次のオプションキーを持つオブジェクトです。

get
プロパティの getter として提供する関数、あるいは getter がない場合は undefined です (アクセサディスクリプタのみ)。既定値は undefined です。
set
プロパティの setter として提供する関数、あるいは setter がない場合は undefined です (アクセサディスクリプタのみ)。既定値は undefined です。

Bear in mind that these options are not necessarily own properties so, if inherited, will be considered too. In order to ensure these defaults are preserved you might freeze the Object.prototype upfront, specify all options explicitly, or point to null as __proto__ property.

// using __proto__
Object.defineProperty(obj, "key", {
  __proto__: null, // no inherited properties
  value: "static"  // not enumerable
                   // not configurable
                   // not writable
                   // as defaults
});

// being explicit
Object.defineProperty(obj, "key", {
  enumerable: false,
  configurable: false,
  writable: false,
  value: "static"
});

// recycling same object
function withValue(value) {
  var d = withValue.d || (
    withValue.d = {
      enumerable: false,
      writable: false,
      configurable: false,
      value: null
    }
  );
  d.value = value;
  return d;
}
// ... and ...
Object.defineProperty(obj, "key", withValue("static"));

// if freeze is available, prevents the code to add
// value, get, set, enumerable, writable, configurable
// to the Object prototype
(Object.freeze||Object)(Object.prototype);

プロパティの生成

指定されたプロパティがオブジェクトに存在しないとき、Object.defineProperty() は指定されたかたちで新たなプロパティを生成します。ディスクリプタでフィールドを記述しないことができ、そのようなフィールドは既定値にします。真偽値をとるフィールドはすべて、既定値が false です。valueget、および set の各フィールドの既定値は undefined です。

var o = {}; // 新しいオブジェクトの生成

// データディスクリプタにより、defineProperty を用いてオブジェクトプロパティを追加する例
Object.defineProperty(o, "a", {value : 37,
                               writable : true,
                               enumerable : true,
                               configurable : true});
// o オブジェクトに 'a' プロパティが存在し、その値は 37 です

// アクセサディスクリプタにより、defineProperty を用いてオブジェクトプロパティを追加する例
var bValue;
Object.defineProperty(o, "b", {get : function(){ return bValue; },
                               set : function(newValue){ bValue = newValue; },
                               enumerable : true,
                               configurable : true});
o.b = 38;
// o オブジェクトに 'b' プロパティが存在し、その値は 38 です
// o.b は再定義されない限り、その値は常に bValue と同じです。

// 両方を混在させることはできません:
Object.defineProperty(o, "conflict", { value: 0x9f91102, 
                                       get: function() { return 0xdeadbeef; } });
// TypeError が発生します: value はデータディスクリプタにのみ、get はアクセサディスクリプタにのみ存在します

プロパティの変更

プロパティがすでに存在するとき、Object.defineProperty() はディスクリプタ内の値および現在のオブジェクトの設定に基づいて、プロパティの変更を試みます。旧ディスクリプタで configurable 属性が false (プロパティは "non-configurable") である場合、writable であるものを除き属性を変更できません。この場合、データ/アクセサの各プロパティタイプを切り替えることもできません (get/set/value/writable なしに定義されたプロパティは "generic" と呼ばれ、データディスクリプタに位置づけられます)。

プロパティが configurable ではない場合、その writable 属性は false にのみ変更できます。

configurable ではないプロパティを変更するとき、それが writable であるか現在の値と新しい値が同じである場合を除き、TypeError が発生します。

writable 属性

writable プロパティ属性が false に設定されているとき、そのプロパティは "non-writable" になります。これは代入できません。

var o = {}; // 新しいオブジェクトの生成

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

console.log(o.a); // logs 37
o.a = 25;
console.log(o.a); // logs 37. 代入文は動作しません。また、エラーは発生しません (strict モードでは発生します)

例で見たように、書き込み不可のプロパティに書き込もうとしても変更されず、またエラーは発生しません。

enumerable 属性

enumerable プロパティ属性は、プロパティが for...in ループに現れるか否かを定義します。

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);  
}
// logs 'a' and 'd' (順不同)

configurable 属性

configurable 属性は、プロパティをオブジェクトから削除できるかとプロパティの属性 (writable 以外) を変更できるかを同時に制御します。

var o = {};
Object.defineProperty(o, "a", { get : function(){return 1;}, 
                                configurable : false } );

Object.defineProperty(o, "a", {configurable : true}); // TypeError が発生
Object.defineProperty(o, "a", {enumerable : true}); // TypeError が発生
Object.defineProperty(o, "a", {set : function(){}}); // TypeError が発生 (set は未定義であった)
Object.defineProperty(o, "a", {get : function(){return 1;}}); // TypeError が発生 (新たな get は全く同じであるにもかかわらず)
Object.defineProperty(o, "a", {value : 12}); // TypeError が発生

console.log(o.a); // logs 1
delete o.a; // 何も起きません
console.log(o.a); // logs 1

o.aconfigurable 属性が 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});

クロスブラウザに於けるの懸念事項

Array オブジェクトの length プロパティの再定義

配列の length プロパティを再定義することは可能ですが、すべてのブラウザでこの再定義が可能ではありません。配列の length プロパティの再定義の試みに対して、Firefox 4 では TypeError が発生します。Object.defineProperty() を実装する Chrome のバージョンでは一部の状況において、配列の現在の length プロパティと異なる値の length を無視し、また書き込み許可の変更は一部の状況において暗黙的に動作しないようです。Object.defineProperty() を実装する Safari のバージョンでは配列の現在の length プロパティと異なる値の length を無視し、また書き込み許可を変更する試みはエラーなしに実行されますが実際はプロパティの書き込み許可が変更されません。Internet Explorer 9 およびそれ以降のみが、完全かつ正確に配列の length プロパティの再定義を実装しているようです。現時点では、配列の length プロパティの再定義はどのブラウザでも動作する、あるいは特定のルールに則って動作するとは考えないようにしてください。

Internet Explorer 8 特有のケース

Internet Explorer 8 は Object.defineProperty() メソッドを DOM オブジェクトでのみ使用できるものとして実装しました。以下 2 点に注意が必要です:

  • ネイティブオブジェクトで Object.defineProperty() を用いようとするとエラーが発生します。
  • プロパティの属性は特定の値を設定しなければなりません。データディスクリプタでは true, true, true、アクセサディスクリプタでは configurable に true、enumerable に false です。ほかの値を与えようとすると、エラーが発生します。
  • プロパティの再設定には、始めにプロパティの削除が必要です。プロパティが削除されていない場合、再設定を試みる前の状態のままになります。

ブラウザの互換性

機能 Firefox (Gecko) Chrome Internet Explorer Opera Safari
基本サポート 4 (2) 5 (前のバージョンではテストされていません) 9 (8、ただし Dom オブジェクトでのみ有効で一部非標準の動作があります。前出の説明を参照。) 11.60 5.1 (5、ただし DOM オブジェクトでは不可)
機能 Firefox Mobile (Gecko) Android IE Mobile Opera Mobile Safari Mobile
基本サポート 4.0 (2) (有) ? 11.50 (有)

Kangax's compat tables に基づきます。

関連情報

Document Tags and Contributors

Contributors to this page: ethertank, yyss
最終更新者: ethertank,