.defineProperty()
オブジェクト上で直接プロパティを新しく定義、または既存のプロパティの修正を行い、そのオブジェクトを返します。
文法
Object.defineProperty(obj, prop, descriptor)
引数 | 説明 |
---|---|
obj | 定義または修正を行うプロパティを持つオブジェクトを指定します。 |
prop | 定義または編集を行うプロパティの名前を指定します。 |
descriptor | 定義または編集されるプロパティへの記述子(descriptor)です。 |
説明
このメソッドを使用して、オブジェクトへプロパティを正確に追加または編集を行うことが可能です。 割りあてによって追加される通常のプロパティには、 プロパティ列挙中(for...inループ、 またはObject.keysメソッド)に表れる事が出来る、 値を変更・削除(delete)することが出来る、といった特性があります。 このメソッドは、それらデフォルトの特性を変更することを可能にしてくれます。
オブジェクトに提供されるプロパティの記述子には、データ記述子とアクセサ記述子の、 主に2つの特性があります。 データ記述子は、書き込み可または不可の値を持つプロパティです。 アクセサ記述子は、getterとsetterのペアの関数によって表されるプロパティです。 記述子は2つのうち1つだけの特性にしなければならず、両方の特性を持つことは出来ません。
データ記述子とアクセサ記述子で共有
データ記述子とアクセサ記述子は、両方ともオブジェクトです。 これらは下記の任意のキーを共有します。
データ記述子
また、データ記述子は下記のキーを持ちます。
アクセサ記述子
また、アクセサ記述子は下記のキーを持ちます。
これらのプロパティは必ずしも自身のプロパティとは限らず、 継承されている可能性があることも考慮するようにしてください。 これらのデフォルトの状態が保護されていることを保証するには、 事前に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
です。
value
、get
とset
フィールドのデフォルトは、
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...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 } );
// 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.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});
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を設定してください。(?) その他の値を提供しようと試みると、エラーがスローされる結果になります。(?) - プロパティの再設定には、先にプロパティを削除する必要があります。 もしプロパティが削除されていないと、再設定を試みる前のままになります。
関連項目
© 2017 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
このページは、ページトップのURL先のMozilla Developer Network(以下、MDN)のコンテンツを翻訳した内容を基に構成されています。 構成について異なる点も含まれますので、下記の項目を確認し、必要に応じて元のコンテンツをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- 特定のブラウザに特化しすぎている情報やあまりにも古い情報、 または試験的に導入されているようなAPIや機能については、省略していることがあります。
- 例やデモについて、実際にページ内で動作させる関係で一部ソースコードを変更している場合や、 その例で使用しているコンテンツの単語や文章などを日本人向けに変更しいてる場合があります。
- MDNの更新頻度が高いため、元のコンテンツと比べ情報が古くなっている可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報の追記を行っている事があります。