こんにちは、ライターのマサトです!
今回は、オブジェクトの中身を抽出したり繰り返し処理を効率よく行える「for – in文」について学習をします!
この記事では、
・「for-in文」とは?
・オブジェクトのプロパティを取得する
・プロパティの「値」を取得する
・breakで途中終了する
という基礎的な内容から、
・配列も「for – in文」で処理できる?
・「for – in文」は継承に注意が必要!
・「hasOwnPropery」の使い方について
・「Object.keys」の使い方について
などの応用的な手法に関しても解説していきます。
この記事で、「for – in文」をしっかり学習して自分のスキルアップを目指しましょう!
「for-in文」とは?
それでは、「for – in文」についての基本的な知識から勉強を始めていきましょう!
「for – in文」はオブジェクトを効率よく扱うための構文なので、まず最初に簡単なオブジェクトを1つ用意しましょう。
var obj = { name: '太郎', age: 45, country: 'Japan' }
この例では、人物の「名前」「年齢」「国」の情報が格納されたオブジェクト「obj」を作成しています。
サンプル例なのでオブジェクトのプロパティが3つしかありませんが、実際には数十個くらいあるものが普通でしょう。
これらのプロパティをすべて抽出したり、各プロパティに対して何らかの処理を実行したい時などに便利なのがこれから学習する「for – in文」というわけです。
一般的な構文としては次のようになります。
for( var 変数 in オブジェクト ) { //ここに繰り返し処理を書く }
「オブジェクト」は先ほどの例で言うと「obj」になり、その中身であるプロパティが1つずつ「変数」に格納されて処理を行うことができるわけです。
オブジェクトのプロパティを取得する
それでは、実際にオブジェクトのプロパティをすべて列挙するプログラムを作ってみましょう!
先ほど作成した「obj」のプロパティを使い、コンソールにすべて出力するには次のように書きます。
for( var item in obj ) { console.log( item ); }
実行結果
name age country
この例で分かるように、変数「item」にオブジェクトのプロパティが1つずつ格納されていくわけです。
そして、列挙できるプロパティがなくなり次第プログラムは自動的に終了するようになっているので便利です。
プロパティの「値」を取得する
今度は、プロパティの「値」を取得しましょう!
書き方は、先ほどのプロパティを取得する例とほとんど同じですが、「値」を表示するには変数に格納されたプロパティを上手く活用しなければいけません。
for ( var item in obj ) { console.log( obj[item] ); }
実行結果
太郎 45 Japan
プロパティを表示するだけなら変数「item」をそのまま使えば良かったわけですが、「値」を取得する場合は「オブジェクト[ プロパティ ]」のような形式で表す必要があるのを覚えておきましょう!
このように、「for – in文」を使ってプロパティやその値を取得できるようになれば、効率よくプログラムを書けるようになるので複雑なオブジェクトも簡単に操作できるようになるでしょう。
breakで途中終了する
「for – in文」は、オブジェクトのプロパティがあるだけ繰り返し処理を行うわけですが、場合によっては途中で終了させたいこともあるでしょう。
そんな時は「break」を使いましょう!
次のコード例では、冒頭で作成したオブジェクト「obj」を使い、すべてのプロパティを出力する前に「break」を使って終了させています。
for( var item in obj ) { console.log( item ); //プロパティが「age」の場合 if(item === 'age') { //処理を途中で終了させる break; } }
実行結果
name age
この例では、途中にプロパティが「age」を参照した時点で「break」によってプログラムの処理を終了させています。
このように、途中で変数の「中身」が目的の「プロパティ」と一致するかどうかを条件分岐させる方法はよく使われるので覚えておきましょう。
配列も「for-in文」で処理できる?
「for – in文」はその性質上、オブジェクトだけでなく配列にも利用することができます。
次の例を見てください。
var array = ['イチゴ', 'バナナ', 'メロン']; for( var item in array ) { console.log( array[item] ); }
実行結果
イチゴ バナナ メロン
この例では、配列「array」の要素を「for – in文」を使ってすべて出力しているのが分かります。
しかしながら、「for – in文」はJavaScriptの実行環境やブラウザのバージョンによって、必ず要素の順番通りに出力されることが保証されていません。
配列の要素を扱う場合は、オブジェクトよりも順番を保証して欲しいケースが多いと思いますので、この場合は素直に「for文」を使いましょう。
var array = ['イチゴ', 'バナナ', 'メロン']; for( var i=0; i<array.length; i++ ) { console.log( array[i] ); }
配列の添字を「for文」で繰り返し処理に使うことで、順番通りに処理を行えるので最も簡単な解決方法と言えますね。
次の記事では、「for文」の基本から応用的な使い方までを「ミニテスト」付きで分かりやすく理解できるので、ぜひ合わせて一読してみてください!
「for – in文」は継承に注意が必要!
さて、「順番」以外にも「for – in文」を扱う上で重要な点として「継承」があります。
言葉で説明すると難しくなるので、まずは次のコード例を見てください!
var obj = { name: '太郎', age: 45, country: 'Japan' } console.log(obj);
実行結果
age:45 country:"Japan" name:"太郎" __proto__:Object
この例では、簡単なオブジェクトを作成してコンソールに出力しています。
3つのプロパティを設定していますが、実行結果には4つ目として「__proto__:Object」が表示されている点に注意が必要です。
これは簡単に言うと「Object」が持つ機能を引き継いで(継承)いますよ…という意味になります。
JavaScriptでは、オブジェクトを作成した時点で自動的にこの「Object」というオブジェクトがもつ機能を引き継ぐことによって、さまざまな標準機能を使えるようになるわけです。
さらにJavaScriptでは、この「Object」に対して独自に機能を拡張することも可能なんですが、ココが「for – in文」を使う上で重要なポイントになります。
次のコード例を見てみましょう!
var obj = { name: '太郎', age: 45, country: 'Japan' } //独自に「empty」という空の関数を定義する Object.prototype.empty = function() {}; for( var item in obj) { console.log( item ); }
実行結果
name age country empty
この例では、「Object」の機能を拡張して新しく「empty」という機能を定義しているのが分かります。
この状態で「for – in文」を使ってこれまで通りプロパティを列挙すると、新しく定義した「empty」まで一緒に出力されているのが分かりますね。
このように「for – in文」は継承元である親オブジェクトも一緒に列挙可能なプロパティを取得してしまうので、場合によっては意図しない挙動になる可能性があるわけです。
さらに、JavaScriptでよくあるケースとして、HTML要素を取得する場合などもこの「継承」を意識する必要があります。
次のコード例では、「pタグ」要素をすべて取得してコンソールに出力しています。
<body> <p>テキスト1</p> <p>テキスト2</p> <p>テキスト3</p> <script> var p = document.getElementsByTagName( 'p' ); console.log( p ); </script> </body>
実行結果
length: 3 0: p 1: p 2: p __proto__:HTMLCollection
実行結果からも分かるように、HTML要素を取得した場合には「HTMLCollection」というオブジェクトが持つ機能を継承しているわけです。
そのため、このまま「for – in文」を利用すると、次のような結果になります。
var p = document.getElementsByTagName('p'); for( var item in p ) { console.log( p[item] ); }
実行結果
<p>テキスト1</p> <p>テキスト2</p> <p>テキスト3</p> 3 function item() { [native code] } function namedItem() { [native code] }
実行結果を見ると、p要素以外にもいくつか継承されている機能が列挙されているのが分かりますね。
このように「for – in文」が持つ特性を理解していないと、予想していなかったプロパティまで列挙してしまい、挙動がおかしくなってしまうプログラムを作ることになってしまいます。
そこで、簡単な解決方法として「継承」を意識せずにプロパティを列挙できる方法が2通りあるのでご紹介します!
「hasOwnProperty」の使い方について!
まず最初に、最も簡単に使える「hasOwnProperty」のご紹介です!
これは、「for – in文」の繰り返し処理の中で、継承元のプロパティかどうかを条件分岐することで必要なプロパティだけを抽出する方法になります。
先ほどのHTMLを取得する例を書き換えると、次のようになります!
var p = document.getElementsByTagName( 'p' ); for( var item in p ) { // そのオブジェクトの直接のプロパティのみを取得する if( p.hasOwnProperty( item ) ) { console.log( p[item] ); } }
実行結果
<p>テキスト1</p> <p>テキスト2</p> <p>テキスト3</p>
このように書くことで、出力結果は見事に「pタグ」の要素だけになっていますね!
通常の「for – in文」に条件分岐を加えるだけなので、扱いが簡単でコードの修正も手軽に行えるのが特徴でしょう。
「Object.keys」の使い方について!
もう1つ、継承を意識しない書き方として「Object.keys()」を使う方法があります。
次のコード例を見てください!
var p = document.getElementsByTagName("p"); //プロパティのみを取得する var keys = Object.keys(p); for( var item in keys ) { console.log( p[item] ); }
実行結果
<p>テキスト1</p> <p>テキスト2</p> <p>テキスト3</p>
この例では、「Object.keys( p )」と記述することでそのオブジェクトが持つプロパティを自動的にすべて取得し、各プロパティを配列要素に格納しているのが分かります。
この配列を使って「for – in文」でプロパティを列挙すれば、実行結果のようにpタグ要素だけを出力できるというわけです。
ただし、せっかく配列に格納されているので、無理に「for – in文」を使わなくても「forEach文」などを活用しても同じ結果になります。
var p = document.getElementsByTagName("p"); Object.keys(p).forEach( function(value) { console.log( p[value] ); })
実行結果
<p>テキスト1</p> <p>テキスト2</p> <p>テキスト3</p>
そもそも「forEach文」は配列を扱うためのメソッドなので、この場合であれば「for – in文」よりも柔軟に対応できるコードと言えるでしょう。
まとめ
今回は、オブジェクトを効率よく扱える「for – in文」について学習してきました。
主に、オブジェクトの「プロパティ」や「値」を使って繰り返し処理を行う際に便利な構文で、気をつけるべきポイントは「継承元のプロパティ」も一緒に列挙されてしまうという点でした。
「for – in文」を使う際には、「hasOwnProperty」や「Object.keys」も重要な選択肢になることを忘れないように気をつけましょう!