【JavaScript】 プロパティのデータ構造
更新日:2021/02/05
JavaScriptのプロパティは、オブジェクトリテラルや. (ドット)表記などで簡単に定義することができます。
しかし書き込みを制御したいときなどもっと細やかな設定が必要なときは、属性についての知識が必要になります。
プロパティの種類
オブジェクト指向言語でのプロパティは、一般的にはオブジェクトの属性や状態を表すと説明されてることが多いです。
この説明だと少し抽象的なので、理解しにくいと思います。
プロパティを持つ言語の実際の機能を見てみると、オブジェクトから値を入出力することを目的としたオブジェクトメンバーを指すことが多いです。
このとき、プロパティ名の持つ意味と入出力される値に、何らかの関連があることが望ましいとされています。
例えば、nameプロパティは名前を表す値を取り扱うなどです。
JavaScriptも同様ですが、JavaScriptのプロパティはデータプロパティとアクセサープロパティの2種類にわけることができます。
データプロパティ
データプロパティはJavaScriptのデフォルトのプロパティで、値をそのまま保存し、保存した値をそのまま返します。
obj.prop = 100; // propプロパティに100を渡す(プロパティはこの値を記憶・保存する)
const data = obj.prop; // propプロパティが保存している値を返す
なお、値として関数オブジェクトを記憶している場合、そのプロパティをメソッドと呼びます。
obj.prop = function(){}; // obj.propはメソッド
多くのオブジェクト指向言語では、メソッドとプロパティは別のものとして定義されていますが、JavaScriptでは同じデータプロパティです。
アクセサープロパティ
アクセサープロパティは、セッター関数とゲッター関数を定義できるプロパティです。
セッター関数は、受け取った値を型チェックしたり任意の形式に加工して保存します。
ゲッター関数は、保存されている一つまたは複数の変数や固定値を任意に組み合わせて返します。
次のように、setまたはgetキーワードを使用しすると、アクセサープロパティが作成されます。
function getObj(){
let _data=0;
return {
set prop( val ){
if( !Number.isFinite( val ) ) // 数値以外はエラー
throw new Error("数値ではない!!");
_data = val;
},
get prop( ){
return "現在の値は" + _data + "です";
}
}
}
const obj = getObj();
obj.prop = 5963;
console.log( obj.prop ); // 現在の値は5963です
obj.prop = "text"; // エラー:Uncaught Error: 数値ではない!!
セッター・ゲッターについては、次のページを読んでみてください。
■【JavaScript】 ゲッター・セッターとは?必要性はあるのか?
プロパティの内部データ構造
僕がデータの実体とプロパティの関係を説明するとき、データには識別ラベルのようなものが割り当てられていて、プロパティはそのラベルを保持していると解説しています。
代入などが行われると、データの実体がやりとりされるのではなくて、ラベルのようなものがやりとりされるわけです。
実際のプロパティは、ラベルだけでなく、いくつかの属性を所持しています。
ただし、データプロパティとアクセサープロパティで異なる属性を持っています。
データプロパティの内部属性
データプロパティは、全部で4つの属性を持っています。
属性名 | 値 | 通常値 | 説明 |
---|---|---|---|
value | オブジェクト ・プリミティブ | 指定値 | プロパティの値 |
writable | true または false | true | value属性の値 を変更できるか |
enumerable | true または false | true | for-in文や Object.keys()で |
configurable | true または false | true | 属性の変更が できるかどうか |
通常値は、オブジェクトリテラルや .(ドット)表記などの通常の方法でプロパティを作成したときにセットされる値です。
通常のプロパティ属性値
obj.prop = 1; // value : 1
// writable: true
// enumerable: true
// configurable: true
属性を変更するときは、Object.defineProperty()を使用します。
value属性
value属性は、実データへの参照(識別ラベルのようなもの)を保持しています。
代入等の操作は、value属性が変更されます。
writable属性
この属性がfalseのとき、value属性の値を他のものに置き換えることができません。
つまりプロパティ値を上書き禁止にできます。
enumerable属性
この属性がfalseのとき、for-in文やObject.keys()で列挙されません。
const obj = {
prop1:123,
};
Object.defineProperty( obj , "prop2" ,
{enumerable:false,value:456}
);
Object.keys( obj ).forEach(
e => console.log( e ) // prop1のみ列挙される
);
これはfor-in文やObject.keys()などの特定の方法での列挙ができるかどうかを制御するフラグです。
プログラムコードからプロパティを隠すことを目的としたものではありません。
例えば、Object.getOwnPropertyNames( )を使用すると、enumerable属性がfalseのプロパティも列挙されます。
const obj = {
prop1:123,
};
Object.defineProperty( obj , "prop2" ,
{enumerable:false,value:456}
);
Object.getOwnPropertyNames( obj ).forEach(
e => console.log( e ) // prop1、prop2が列挙される
);
configurable属性
この属性がfalseのとき、データプロパティからアクセサープロパティへ変更できません。
また、configurableとenumerable属性を変更できません。
writable属性をtrueからfalseに変更可能ですが、falseからtrueに変更できません。
アクセサープロパティの内部属性
アクセサープロパティは、全部で4つの属性を持っています。
属性名 | 値 | 初期値 | 説明 |
---|---|---|---|
get | 関数オブジェクト | undefined | 値を出力するゲッター関数 |
set | 関数オブジェクト | undefined | 値を入力するセッター関数 |
enumerable | true または false | true | for-in文や Object.keys()で |
configurable | true または false | true | 属性の変更が できるかどうか |
get/set属性
setとgetキーワードを使用して定義された同名のセッター・ゲッター関数は、同じプロパティの属性としてセットされます。
enumerable属性
データプロパティのenumerable属性と同様に、この属性がfalseのとき、for-in文やObject.keys()で列挙されません。
configurable属性
この属性がfalseのとき、アクセサープロパティからデータプロパティから変更できません。
また、get、set、configurable、enumerable属性を変更できません。
プロパティ定義と属性変更
プロパティの属性をデフォルト値以外で定義する場合、Object.defineProperty()を使用します。
定義
Object.defineProperty( オブジェクト , プロパティ名 , プロパティ記述子 )
Object.definePropertyについては、次の記事で解説しています。
■【JavaScript】 definePropertyメソッドとは?通常のプロパティ追加との違い
ここでは、使用例のみ掲載します。
最初は単純なデータプロパティ定義です。
value属性のみを指定すると、上書き・列挙・属性変更全てが不可になります。
使用例1:データプロパティ
const obj = {};
Object.defineProperty(obj, "prop", {
value:100,
});
value値に関数を指定すると、メソッドが定義されます。
使用例2:データプロパティ(メソッド)
const obj = {};
Object.defineProperty(obj, "prop", {
value:function (){},
});
getまたはset属性を使用することで、アクセサープロパティを定義できます。
次の例は、アクセサープロパティで扱う値を、オブジェクトのデータプロパティに保存しています。
使用例3:アクセサープロパティ
const obj = {};
Object.defineProperty(obj, "name", {
value:"",
writable:false
});
Object.defineProperty(obj, "prop2", {
get:function (){ return this.name; },
set:function ( val ){ this.name = val; },
writable:false
});
次の例は、アクセサープロパティで扱うデータの保存先を隠蔽しています。
隠蔽については次のページを読んでみてください。
■【JavaScript】 JSにおけるカプセル化手法
使用例4:アクセサープロパティ(クロージャ使用)
function makeObj(){
let name;
const obj = {};
Object.defineProperty(obj, "name", {
get:function (){ return name; },
set:function ( val ){
if( typeof (obj) !== "string" && !( obj instanceof String ) )
throw new Error("文字列ではない!!");
name = val;
},
writable:false
});
}
プロパティに関する標準メソッド
JavaScriptにはObject.defineProperty以外にも、プロパティを操作するためのメソッドをいくつか用意されています。
詳しくは次のページを読んでみてください。
更新日:2021/02/05
関連記事
スポンサーリンク
記事の内容について
こんにちはけーちゃんです。
説明するのって難しいですね。
「なんか言ってることおかしくない?」
たぶん、こんなご意見あると思います。
裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。
そんなときは、ご意見もらえたら嬉しいです。
掲載コードについては事前に動作確認をしていますが、貼り付け後に体裁を整えるなどをした結果動作しないものになっていることがあります。
生暖かい視線でスルーするか、ご指摘ください。
ご意見、ご指摘はこちら。
https://note.affi-sapo-sv.com/info.php
このサイトは、リンクフリーです。大歓迎です。