オブジェクトとプロパティの扱い方【開眼JavaScriptまとめ】
目次
前回のブログではJavaScriptにおいて、オブジェクトとは何なのかということを説明しました。そして、今回はオブジェクトと、それに付随してプロパティの使い方を解説していきます。
また、今回も開眼JavaScriptの内容を自分なりにまとめたものになります。
開眼JavaScriptとは
最初に本記事の参考文献である開眼JavaScriptという技術書について軽く説明したいと思います。
開眼JavaScriptの正式なタイトルは”開眼JavaScript 言語仕様から学ぶJavaScriptの本質”といいます。
そのタイトル通りではありますが、JavaScriptの言語特性で知っておくべきポイントを纏めた書籍です。挫折しがちな点である、this、プロトタイプチェーン(継承)、スコープチェーンなどが、サンプルを使って説明されています。
JavaScriptのバージョンはES3を参考に書かれているため、古い仕様ではありますが、JavaScriptの本質的な理解を助けてくれる1冊です。
オブジェクトははすべての値をプロパティとして格納できる
オブジェクトは、すべてのJavaScriptの値を自身のプロパティとして格納できます。次の例ではtheObject
という Object()
オブジェクトを生成し、JavaScriptで使用されるいくつかの型の値をプロパティとして追加します。
上記で設定したそれぞれの値をコンソールに出力すると、
このように格納できていることが分かると思います。この操作は全てのオブジェクトに対して、同様の操作ができます。例えば、関数オブジェクトでも同様の操作が可能です。
つまり、JavaScriptのオブジェクトはJavaScriptで表すことのできるあらゆる値を格納(もしくは参照)することができます。推奨はされていませんが、ネイティブオブジェクトでさえも編集可能です。この、いつでも編集できる性質のことをミュータブル(可変)と呼びます。
プロパティへのアクセス方法
次は、基礎となるプロパティへのアクセス方法を説明します。
オブジェクトのプロパティはドット記法、もしくはブラケット記法で取得・設定・更新することができます。一般にはドット記法が使用されますが、今回はどちらも解説します。
また、delete演算子を使ってプロパティを削除できます。こちらも解説します。
ドット記法
ドット記法とはその名の通り、"." を使用してプロパティにアクセスする方法です。次の例ではドット記法を使用して、プロパティの取得・設定・更新を行っています。
// madaraオブジェクトを生成
let madara = {};
//ドット記法でプロパティを設定
madara.gender = male;
madara.age = 21;
//プロパティの取得
console.log(
madara.gender,
madara.age
); // 結果:male 21
//プロパティの更新
madara.gender = female;
madara.age = 50;
console.log(
madara.gender,
madara.age
); // 結果:female 50
ドット記法ではドット1つの単純な記述でプロパティにアクセスできます。
ブラケット記法
ブラケット記法は記述の単純さでドット記法に劣ります。そのため、一般的には使用されません、しかし、どうしてもブラケット記法をしようしなければいけない場面や、使用したほうが便利な場面が存在します。次の例では、先程のドット記法の例をブラケット記法に置き換えています。
// madaraオブジェクトを生成
let madara = {};
//ドット記法でプロパティを設定
madara["gender"] = male;
madara["age"] = 21;
//プロパティの取得
console.log(
madara["gender"],
madara["age"]
); // 結果:male 21
//プロパティの更新
madara["gender"] = female;
madara["age"] = 50;
console.log(
madara["gender"],
madara["age"]
); // 結果:female 50
ここまでであれば、プラケット記法はドット記法の下位互換でしかありません。しかし、ブラケット記法は変数に格納された文字列を使ってプロパティにアクセスできます。また、JavaScript予約語などの不正なプロパティ名を持つプロパティにアクセスすることもできます。
次はドット記法よりブラケット記法が優れている点として変数に格納された文字列を使う方法と不正なプロパティ名を持つプロパティにアクセスできる点の例を紹介します。
const fooBar = { foobar : "foobar" };
const foo = "foo";
const bar = "bar";
console.log(fooBar[foo + bar]); // 結果:foobar
const object = { "class" : "foo", "123" : "bar" };
// 数値で始まるプロパティ名は不正、"class"はJavaScript予約後のためブラケット記法のみでアクセス可能
console.log(object["123"], object["class"],); // 結果:bar foo
// ドット記法を使用するとエラーが出力
console.log(object.123, object.class); // 結果:エラー
ブラケット記法は基本は使いませんが、こういった特殊な場面では必要になることがあります。
delete演算子
最後はプロパティを削除する方法を紹介します。それはdelete演算子を使うことです。delete演算子は、指定したプロパティをオブジェクトから完全に削除することができます。
次の例では、barプロパティをfooオブジェクトから削除します。
const foo = { bar : "bar" };
delete foo.bar;
console.log("bar" in foo); // 結果:false
このようにdelete演算子でプロパティを削除できました。
delete演算子にはいくつか注意点があります。解説します。
- delete演算子は指定されたオブジェクトのプロパティのみを削除する。プロトタイプチェーンで継承するプロパティを削除しない。
- delete演算子はオブジェクトからプロパティを削除する唯一の方法である。プロパティの値に
undefined
やnull
を指定しても、それはプロパティの値を変更したに過ぎない。 - delete演算子はvarで宣言した変数やfunction文で宣言した関数には適用されない
- delete演算子で配列の要素を削除できる。その場合、指定した配列の要素はundefinedとなり、lengthは維持される。
プロパティが参照されるまでに何が起こっているか
次にプロパティにアクセスしようとしたときに、オブジェクト内にアクセスしようとしたプロパティが存在しなかった場合、JavaScriptでは常にプロトタイプチェーンををたどってプロパティを検索します。
プロトタイプチェーンとはなんぞや?となると思いますので簡単に解説します。
JavaScriptではあるオブジェクトのプロパティにアクセスしようとすると、そのプロパティが存在する場合はそのプロパティの値を返します。その際には、プロトタイプチェーンは行いません。
プロトタイプチェーンはオブジェクトのプロパティにアクセスしようしたときに、そのプロパティが存在しなかった場合に発生します。アクセスしようとしたプロパティが存在しなかった場合、JavaScriptではアクセスしようとしたプロパティのオブジェクトのコンストラクター関数のprototypeオブジェクトを参照します。
すべてのオブジェクトインスタンスは、インスタンスを生成したコンストラクター関数のprototypeプロパティにアクセスするための秘密のプロパティ(__proto__)を持っています。
上記のような、文字を見たことがある人は多いと思います。オブジェクトや配列をconsoleに表示すると、表示されるアレです。この秘密のプロパティをを使って、インスタンスのコンストラクター関数のprototypeプロパティを取得することができます。このプロトタイプチェーンは、自分でコンストラクター関数を定義した場合であっても動作します。
次に、プロトタイプチェーンの実際の動作の例を使って、プロトタイプチェーンを見ていこうと思います。ここでは例として、配列を生成してみます。
const array = ["foo", "bar"];
console.log(array.join()); // 結果:動作する
配列arrayには、joinメソッドを指定してないはずなのに、array.join()
が動作しました。このjoinメソッドはarrayオブジェクト自身で持っているわけではなく、JavaScriptが「他のどこか」から探してきたメソッドになります。この「他のどこか」を探すという行為こそが、プロトタイプチェーンになります。
Arrayオブジェクトのインスタンスから行われるプロトタイプチェーンの場合、Array.prototypeが最初の「他のどこか」になります。
勘の良い方はお気づきかもしれませんが、最初の「他のどこか」ということは最後の「他のどこか」が存在するということです。そして、プロトタイプチェーンは1回であるとは限らないということです。JavaScriptはその関数が更にコンストラクターを持つ限り、プロトタイプチェーンを遡ります。
では、その最後の「他のどこか」とは一体どこなのでしょうか?
プロトタイプチェーンの終着点はObject.prototypeです。その先に検索可能なprototypeプロパティは存在しません。その理由はすべてのprototypeプロパティはオブジェクトであるためです。つまり、Array.prototypeもObjectコンストラクター関数によって定義されているということです。そのため、上記のような例では、Array.prototypeの次にObject.prototypeを検索しています。
"Array.prototypeもObjectコンストラクター関数によって定義されている"というのは下の画像のようなイメージです。
どうでしょうか?理解できたでしょうか?
関数のprototypeについてはまた別の機会に詳しく記事を作成したいと思います。よければそちらも呼んでいただけると幸いです。
まとめ
- オブジェクトは、すべてのJavaScriptの値を自身のプロパティとして格納できる
- JavaScriptのオブジェクトはミュータプル(可変)である
- プロパティへのアクセスはドット記法、ブラケット記法を使って記述できる
- delete演算子を使用することでプロパティを削除できる
- プロパティが参照されるまでにはプロトタイプチェーンが行われる
- プロトタイプチェーンとは、JavaScriptがプロパティを「他のどこか」から探す仕組みのこと
- プロトタイプチェーンの終着点はObject.prototypeである
いかがでしたでしょうか?プロトタイプチェーンはとても難しい概念ですが、JavaScriptを理解するには必須の概念でもあります。逆に理解できれば、JavaScriptのことがかなり優しく感じると思うので、興味のある方は自分で調べてみると良いかもしれません。