.defineProperty()

オブジェクト上で直接プロパティを新しく定義、または既存のプロパティの修正を行い、そのオブジェクトを返します。

文法

Object.defineProperty(obj, prop, descriptor)
引数 説明
obj 定義または修正を行うプロパティを持つオブジェクトを指定します。
prop 定義または編集を行うプロパティの名前を指定します。
descriptor 定義または編集されるプロパティへの記述子(descriptor)です。

説明

このメソッドを使用して、オブジェクトへプロパティを正確に追加または編集を行うことが可能です。 割りあてによって追加される通常のプロパティには、 プロパティ列挙中(for...inループ、 またはObject.keysメソッド)に表れる事が出来る、 値を変更・削除(delete)することが出来る、といった特性があります。 このメソッドは、それらデフォルトの特性を変更することを可能にしてくれます。

オブジェクトに提供されるプロパティの記述子には、データ記述子とアクセサ記述子の、 主に2つの特性があります。 データ記述子は、書き込み可または不可の値を持つプロパティです。 アクセサ記述子は、getterとsetterのペアの関数によって表されるプロパティです。 記述子は2つのうち1つだけの特性にしなければならず、両方の特性を持つことは出来ません。

データ記述子とアクセサ記述子で共有

データ記述子とアクセサ記述子は、両方ともオブジェクトです。 これらは下記の任意のキーを共有します。

configurable

初期値:false

このタイプのプロパティ記述子を変更可能であり、属するオブジェクトから、そのプロパティを削除可能である場合にtrueにします。

enumerable

初期値:false

対応するオブジェクト上で、プロパティの列挙中にこのプロパティを表示させる場合にtrueにします。

データ記述子

また、データ記述子は下記のキーを持ちます。

value

初期値:undefined

プロパティに割り当てられる値です。 正当なJavaScriptの値を指定することが出来ます。(数値、オブジェクト、関数等)

writable

初期値:false

trueの場合、代入演算子によるプロパティの値の変更を可能にします。

アクセサ記述子

また、アクセサ記述子は下記のキーを持ちます。

get

初期値:undefined

プロパティへのgetterを提供する関数、またはgetterを提供しないのであればundefinedを指定します。 この関数の戻り値が、プロパティの値として使用されます。

set

初期値:undefined

プロパティへのsetterを提供する関数、またはsetterを提供しないのであればundefinedを指定します。 この関数はプロパティへ割り当てられる新しい値を、唯一の引数として受け入れます。

これらのプロパティは必ずしも自身のプロパティとは限らず、 継承されている可能性があることも考慮するようにしてください。 これらのデフォルトの状態が保護されていることを保証するには、 事前にObject.prototypeをfreezeして、 明示的に全てのオプションを指定するか、または__proto__にnullを指定させます。

// __proto__を使用
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;
}
// ... and ...
Object.defineProperty(obj, "key", withValue("static"));

// freezeが利用可能であれば、
// Objectのprototypeへの
// コードによる値、get、set、
// enumerable、writable、configurable
// の追加を防ぐことが出来ます。
(Object.freeze||Object)(Object.prototype);

もし、binary-flags-likeな文法でObject.definePropertyを使用した居場合は、 Additional examplesを参照してください。

プロパティの作成

指定されたプロパティがオブジェクト内に存在しない場合、 Object.defineProperty()は新しいプロパティであるとして作成します。 その記述子からフィールド(訳注: enumerable、value等であると考えられる)を省略することが可能で、 その場合はデフォルト値が割り当てられます。 真偽値のフィールドのデフォルトは、falseです。 valuegetsetフィールドのデフォルトは、 undefinedです。 get/set/value/writable 無しで定義されるプロパティは"generic"(一般的/汎用的)と呼ばれ、 データ記述子として"typed"(型付け)されているものとなります。(翻訳に自信なし)

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に設定していた場合(そのプロパティは設定不可ということ)は、 その属性はwritableを除き、変更することが出来ません。 その場合、データとアクセサのプロパティのタイプを互いに切り替えることも出来ません。

プロパティが設定不可(configurableがfalse)の場合に出来る事は、 writable属性をfalseに変更することだけです。

現在の値と新しい値が同じでは無い限り、設定不可のプロパティ属性(writable属性は除く)を変更しようとすると、 TypeErrorがスローされます。

writable属性

writableプロパティ属性にfalseが設定された場合、 プロパティは"書き込み不可"になり、値を再割当てすることが出来なくなります。

var o = {}; // 新しいオブジェクトを作成します。

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

console.log(o.a); // 37を出力します。
o.a = 25; // エラーのスローは発生しません。
          // strictモード時は例え同じ値であっても、スローされます。
console.log(o.a); // 37を出力します。上記の割り当ては処理されません。

この例の通り、書き込み不可のプロパティへの書き込みによって変更されませんが、 エラーがスローされることもありません。

enumerable属性

enumerableプロパティ属性は、 for...inObject.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 } );

// TypeErrorをスロー
Object.defineProperty(o, "a", {configurable : true});
// TypeErrorをスロー
Object.defineProperty(o, "a", {enumerable : true});
// TypeErrorをスロー (事前にundefinedが設定)
Object.defineProperty(o, "a", {set : function(){}});
// throws a TypeError (全く同じ事を行うgetを指定しても)
// TypeErrorをスロー (事前にundefinedが設定)
Object.defineProperty(o, "a", {get : function(){return 1;}});
// TypeErrorをスロー
Object.defineProperty(o, "a", {value : 12});

console.log(o.a); // ログ出力1
delete o.a;       // 何も起こりません
console.log(o.a); // ログ出力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});

SetterとGetterのカスタマイズ

下記の例は、自己アーカイブするオブジェクトの実装方法を示しています。 temperatureプロパティがsetされると、 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}]

仕様

ブラウザ互換性

デスクトップ
機能 Chrome Firefox
(Gecko)
IE Opera Safari
基本 5(それ以前は未調査) 4.0 (2) 9 (8はDOM上でのみ有効であり、非標準な振る舞いをします) 11.6 5.1 (5はDOMオブジェクト上で有効ではありません)
モバイル
機能 Android Firefox
Mobile
IE
Mobile
Opera
Mobile
Safari
Mobile
基本 4.0 (2) 9 11.5

Based on Kangax's compat tables.

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

通常は再定義に制限のある、配列のlengthプロパティを再定義することが可能です。 (lengthプロパティは、設定不可(non-configurable)、列挙不可(non-enumerable)、書き込み可(writable)で初期化されます。 そのため元のままの配列上でlengthプロパティの値の変更、または書き込み不可(non-writable)にすることが可能です。 列挙可、設定可を変更することは出来ず、また書き込み不可(non-writable)であれば、その値を変更すること、 または書き込み可にすることは出来ません。 ただし、全てのブラウザでこれを再定義出来るわけではありません。

Firefox4から22までは、配列のlengthプロパティの再定義を試みると、 必ず(それが許可されていてもいなくても)TypeErrorがスローされます。

Object.defineProperty()が実装されているバージョンのChromeでは、 ある状況では配列の現在のlengthプロパティとは異なるlengthの値を無視します。 また、ある状況で書き込み可への変更が、暗黙的(例外のスロー無し)に処理されないことがあるようです。 これに関連して、Array.prototype.pushのような配列を変化させる幾つかのメソッドでは、 lengthの書き込み不可(non-writable)を尊重しません。

Object.defineProperty()が実装されているSafariのバージョンでは、 配列の現在のlengthプロパティとは異なるlengthの値を無視し、 書き込み可の変更を実行しようとするとエラーは発生しませんが、実際には変更されません。

Internet Explorer 9以降とFirefox 23以降のみ、 配列のlengthプロパティの再定義を十分に正しく実装していると思われます。 現状では、配列のプロパティの再定義が動作する、または特定の方法で動作することに依存するような処理を書かないようにして下さい。 またそれに依存出来る状況であっても、お勧め出来ない理由があります。 (参照: the length property of an array can be made non-writable (but you shouldn’t do it))

Internet Explorer 8特有の注意事項

Internet Explorer 8にはObject.defineProperty()メソッドが定義されていますが、 これはDOMオブジェクト上でしか使用することが出来ません。 また、気をつけるべきことが幾つかあります。

  • ネイティブオブジェクト上でObject.defineProperty()を使用しようとすると、エラーがスローされます。
  • プロパティ属性は何からの値を設定しなければいけません。 データ記述子にはtrue、true、trueを、 アクセサ記述子はconfigurableにtrueを、enumerableにfalseを設定してください。(?) その他の値を提供しようと試みると、エラーがスローされる結果になります。(?)
  • プロパティの再設定には、先にプロパティを削除する必要があります。 もしプロパティが削除されていないと、再設定を試みる前のままになります。

関連項目

 Back to top

© 2017 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.

このページは、ページトップのURL先のMozilla Developer Network(以下、MDN)のコンテンツを翻訳した内容を基に構成されています。 構成について異なる点も含まれますので、下記の項目を確認し、必要に応じて元のコンテンツをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • 特定のブラウザに特化しすぎている情報やあまりにも古い情報、 または試験的に導入されているようなAPIや機能については、省略していることがあります。
  • 例やデモについて、実際にページ内で動作させる関係で一部ソースコードを変更している場合や、 その例で使用しているコンテンツの単語や文章などを日本人向けに変更しいてる場合があります。
  • MDNの更新頻度が高いため、元のコンテンツと比べ情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報の追記を行っている事があります。