プログラムを作る時、オブジェクトやオブジェクトのプロパティが存在するかどうかをチェックしたいという時があるかと思います。
そんな時、グローバル変数の「undefined」を使って判定するプログラムを書いてしまい、上司から「この書き方はよくない」とダメ出しされた挙げ句、プログラムの書き直しを余儀なくされた、という経験がある方もいらっしゃるのではないでしょうか。
今回の記事では「じゃあ、どのように書き直せばいいの?」といった悩みを解決すべく、JavaScriptでオブジェクトの存在チェックをする際に問題となる点を交えながら解説していきたいと思います。
目次
JavaScriptでオブジェクトの存在チェック 事前知識
オブジェクトの存在チェック方法を学ぶにあたり、オブジェクトの構造を把握しておくことは避けて通れません。
まずは「オブジェクト」についての基本的な知識をまとめておきましょう。
オブジェクトとは?
「オブジェクト」とはプログラム上でデータを格納する手段に使われているもので、「変数」や「配列」と並んでよく使われています。
「変数」には「1つのデータしか格納できない」といった制限、「配列」には「複数のデータを格納できる」といった特徴がありますので、「オブジェクト」の理解を深めたい場合は、それらの特徴と比較し、どういった特徴があるのかをまとめていくのが近道となるでしょう。
例えば、複数のデータを格納したい場合に「配列」を使うと以下のようなコードを記述してデータを格納することになります。
1 |
const array = [‘山田太郎’,’ta7car’,’z(aH6ZZ-s5j_!ZTv’]; |
このコードを見ると、人名らしきデータが格納されていることは分かりますが、後ろのデータについては、英数字が羅列されているだけで、何を意味しているのかわかりません。
ですが、同じデータを「オブジェクト」を使って格納させると、以下のような記述となります。
1 2 3 4 5 6 7 |
const user = { name : ‘山田太郎’, id : ‘ta7ca’, password : ‘z(aH6ZZ-s5j_!ZTv’ } |
「配列」では英数字の羅列にしか見えなかったデータが、「id」や「password」といった名前を持たせることで、どのようなデータなのかを把握できるようになったのではないでしょうか。
このように、JavaScriptでは「名前:値」という構造でデータが格納され、プログラム上で扱いやすく整理されたものを「オブジェクト」と呼んでいます。
「オブジェクト」の厳密な定義とは異なるかもしれませんが、「オブジェクト」の説明については人によって様々ですので、今のところは「名前:値」という構造をもった「データの格納手段」がオブジェクトと呼ばれている、と理解しておくとよいでしょう。
プロパティとメソッド
「名前:値」の構造でデータが格納されているものをオブジェクトと呼んでいる、と説明しましたが、「名前:値」のペアで構成されてさえいれば、配列や関数なども格納することができます。
そして、格納されているものが「値」なのか「関数」なのかによって呼び方が変わり、一般的に、「名前:値」のペアが格納されている場合には「プロパティ」と呼び、「名前:関数」のペアが格納されている場合には「メソッド」と呼んで使い分けています。
この2つの用語は、これからオブジェクトの存在チェックを説明していく上で何度も登場する用語ですので、オブジェクトの基礎知識として把握しておいてください。
オブジェクトの作成・継承
実際のプログラムコードでオブジェクトを作成する時は、以下のようなコードを記述します。
1 |
const obj ={}; |
「名前:値」の構造でデータが格納されているものをオブジェクトと呼ぶと説明してきましたが、このコードのようにプロパティを指定せずともオブジェクトを宣言することは可能です。
なぜなら、プロパティを指定せずに宣言したオブジェクトの場合、裏でこっそり「プロトタイプオブジェクト」からプロパティやメソッドを継承しているためです。
プロトタイプオブジェクトやオブジェクトの継承についての説明をすると長くなってしまいますので、別の機会に説明したいと考えていますが、今回の記事では「空のオブジェクトを作成しても、プロトタイプオブジェクトからプロパティやメソッドを継承している」ということだけでも把握しておいていただければと思います。
オブジェクトの基礎知識については以上となります。
JavaScriptでオブジェクトの存在チェック 未定義のオブジェクトの判定
オブジェクトに関する基本的な知識を把握し、「プロパティ」や「メソッド」といった用語の確認もできたところで、いよいよ「オブジェクトの存在チェック」の説明に移りたいと思います。
undefinedと比較する方法
まずは、オブジェクトが定義されているかどうかを判定する方法を考えていきたいと思います。
JavaScriptでオブジェクトの存在チェックを行う際、グローバル変数として「undefined」という名前の変数が用意されているため、最初に思い浮かべてしまう記述が以下のコードでしょう。
1 2 3 4 5 6 |
var user; if (user === undefined){ console.log('未定義です') }else{ console.log('定義されています') } |
冒頭にも書きましたが、JavaScriptでオブジェクトの存在チェックをする場合において、グローバル変数の「undefined」を使う方法は「あまりよくない書き方」と言われています。
このプログラムを実行すると、コンソールログにuser ===undefinedの時の処理内容である「未定義です」という文字列が出力されるため、一見すると問題のないコードのように思われのですが、この記述では、「同名のローカル変数を作ることができる点」が考慮されていません。
つまり、以下のコードのように「undefined」という同名の変数が作られ、異なる値が入力されてしまうと、未定義の「user」が返す「undefined」と一致せずに「定義されています」という文字列が出力されてしまいます。
1 2 3 4 5 6 7 8 |
var user; var undefined = 1; if (user === undefined){ console.log('未定義です') }else{ console.log('定義されています') } |
通常のプログラミングでは、undefinedという名前でローカル変数を作ったり、そこに異なる値を代入するといった行為自体があり得ないことではありますが、「ローカル変数として作れてしまう」という仕様である以上、その点を考慮にいれておく必要があることは確かでしょう。
typeof演算子を使う方法
グローバル変数の「undefined」を使う方法に問題があることから、オブジェクトの存在チェックをする際に、よく使われているのが「typeof演算子」を使った方法です。
未定義のオブジェクトにtypeof演算子を作用させると文字列の「undefined」が返ってくるため、以下のようなコードを記述することで、オブジェクトが未定義かどうかを確実に判定することができます。
1 2 3 4 5 |
if (typeof user === 'undefined'){ console.log('未定義です') }else{ console.log('定義されています') } |
「undefined」をクォーテーションでくくり、文字列として比較させる点がポイントです。
グローバル変数の「undefined」を使って「あまり良くない書き方」と指摘されたら、こちらの方法を使った内容に変更しておくとよいでしょう。
void演算子を使う方法
他には「void演算子」を使って判定する方法もあります。
voidは、どんな値でも常に「undefined」を返す演算子のため、typeof演算子を使う場合と同様、オブジェクトが未定義かどうかを確実に判定することができます。
1 2 3 4 5 6 |
var user; if (user === void 0){ console.log('未定義です') }else{ console.log('定義されています') } |
サンプルのコードでは「void 0」と記述していますが、どんな値でも「undefined」を返しますので、「void 10」や「void100」と記述しても構いません。「void 0」と記述するのは通例のようです。
==nullを使う方法
「undefined」と「null」を厳格に区別する必要がなければ、「==null」で判定する方法もあります。
1 2 3 4 5 6 |
var user; if (user == null){ console.log('未定義です') }else{ console.log('定義されています') } |
「===」ではなく「==」を使うのがポイントです。
「===null」としてしまうと、「undefined」と「null」を区別してしまうため、想定した結果が得られません。
「==null」を使って「undefined」と「null」を区別せずに判定する必要があります。
typeof演算子が最も安全
以上が、未定義のオブジェクトを判定する方法となりますが、これらの中で最も安全なのは「typeof演算子」を使う方法であると言われています。
その理由は各々のコードを見比べてもらうと分かるかと思いますが、「typeof演算子」を使う方法のみが「var user」で変数を宣言しなくても判定できるのに対し、他の方法では「var user」と記述して変数を宣言しないとエラーとなって処理が止まってしまうことにあります。
実際のプログラムでは「オブジェクトが定義されてなければ変数としても定義されていない」といった状況がほとんどかと思いますので、わざわざ変数を宣言しなくとも判定でき、処理を止める心配のない「typeof演算子」を使った方法が最も安全な方法といえるのではないでしょうか。
JavaScriptでオブジェクトの存在チェック プロパティの存在チェック
それでは最後に、プロパティの存在チェックについて解説したいと思います。
オブジェクト自体は定義されているけど、指定したプロパティ名を保有しているかどうかを判定したい、といった場合の解説となります。
typeof演算子を使う方法
まずは、オブジェクトの存在チェックをする際に、最も安全だった「typeof演算子」を使った方法を見ていきましょう。
以下のようなコードを記述することで、userオブジェクトの中に「familyname 」という名前のプロパティが存在するかをチェックすることができます。
1 2 3 4 5 6 7 8 9 |
var user={ name :’山田太郎’ }; if (typeof user.familyname === 'undefined'){ console.log('未定義です') }else{ console.log('定義されています') } |
ただ、プロパティの存在チェックの場合はオブジェクトが定義済であることが前提となっていますので、他の方法と比べて安全度が高いというわけではありません。
むしろ、後述する「継承元をたどって判定するかどうか」といった点に注意したほうがよいかと思います。
in演算子を使う方法
プロパティの存在チェックをする方法として「in演算子」を使う方法もあります。
「in 演算子」は、オブジェクトの中に指定されたプロパティが存在する場合に true を返しますので、以下のようなコードを記述することでプロパティの存在チェックが可能です。
1 2 3 4 5 6 7 8 9 |
var user={ name :'田中', }; if ( ‘familyname’ in user === false){ console.log('未定義です') }else{ console.log('定義されています') } |
このコードではuserオブジェクトの中に「familyname 」という名前のプロパティが存在するかをチェックすることができます。
hasOwnPropertyメソッドを使う方法
「hasOwnPropertyメソッド」はin演算子と同様、オブジェクトの中に指定されたプロパティが存在する場合に true を返しますので、以下のようなコードを記述することで、プロパティの存在チェックが可能です。
1 2 3 4 5 6 7 8 9 |
var user={ name :'田中', }; if ( user.hasOwnProperty('familyname')=== false){ console.log('未定義です') }else{ console.log('定義されています') } |
ただし、この記述では「オブジェクト内に同名のプロパティを作ることができる点」が考慮されていません。
つまり、以下のコードのように同名の「hasOwnProperty」が作成され、常に「false」を返してしまうような処理がなされてしまうと、想定した結果がえられなくなってしまいます。
1 2 3 4 5 6 7 8 9 10 11 12 |
var user={ name :'田中', hasOwnProperty : function(){ return false; } }; if ( user.hasOwnProperty('familyname')=== false){ console.log('未定義です') }else{ console.log('定義されています') } |
グローバル変数の「undefined」を使って判定する時と同様、「hasOwnPropertyメソッド」を使ってプロパティの存在チェックを行う場合は、「同名のプロパティを作れてしまう」という点を考慮にいれておく必要があるでしょう。
継承元のプロパティを判定
「hasOwnPropertyメソッド」を使った方法だと「同名のプロパティを作れてしまう」ことから、「hasOwnPropertyメソッド」を使った判定よりも、「typeof演算子」「in演算子」を使った判定の方が安全であると言えます。
とはいえ「hasOwnPropertyメソッド」は使わない方がよいかと言われると、そういうわけでもありません。
というのも、プロパティの存在チェックをする際、「継承元にあるプロパティを判定するかどうか」によって違いがあるためです。
その違いがわかるコードを作成しましたので、とりあえず、以下のコードを実行してみましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
var user={}; //判定1 if ( user.hasOwnProperty('toString')=== false){ console.log('判定1:未定義です') }else{ console.log('判定1:定義されています') } //判定2 if('toString' in user === false){ console.log('判定2:未定義です') }else{ console.log('判定2:定義されています') } //判定3 if ( typeof user.toString === 'undefined'){ console.log('判定3:未定義です') }else{ console.log('判定3:定義されています') } |
このコードを実行すると、出力結果は以下のようなものとなります。
1 2 3 |
判定1:未定義です 判定2:定義されています 判定3:定義されています |
1行目で宣言したオブジェクトは何のプロパティも設定していませんが、「toString」という名前のプロパティが存在するかチェックをすると、「hasOwnPropertyメソッド」を使って判定した場合は「未定義です」と出力されています。
ですが、その後に続く「typeof演算子」と「in演算子」を使って判定した場合では「定義済み」と出力されており、この結果からわかることは、「hasOwnPropertyメソッド」を使った方法では「継承したプロパティは判定していない」のに対し、「typeof演算子」と「in演算子」を使った方法では「継承したプロパティも判定している」ということになります。
継承したプロパティを判定の対象とするかどうか「hasOwnPropertyメソッド」「typeof演算子」「in演算子」のどれを使うかを判断していくことになりますので、注意しておくとよいでしょう。
まとめ
いかがでしたか?
今回はJavaScriptでオブジェクトの存在チェックをする様々な方法を紹介させていただきました。
「undefined」や「hasOwnProperty」をオブジェクトの存在チェックに使う場合の注意点が整理できましたでしょうか。
今後、JavaScriptでオブジェクトの存在チェックをしなければならない状況が発生した時に、こちらの記事を思い出していただければ幸いです。