#はじめに
テスト自動化ができる様になりたくて、「Javascript」を勉強していた時に「for – of文 」と「for」文ってどっちも配列の中身を取得できるけど、結局なにが違うの?と迷子になったので、違いの備忘録。
備忘録を書いている間に勉強になる事がいろいろあったので私の思考回路をメモしておきます。
多分初心者は同じ道たどるのかな・・・?
#for文のおさらい
自分で記事を書いているうちに頭パーンしたので、for-of と forの違いをおさらい!!
・for-of文は配列から1要素ずつ取り出して、繰り返しできる構文
1要素取り出す ⇒ {}の中身を実行 ⇒ 次の要素を取り出す ・・・・・
・for文は繰り返しの為の変数を用意して継続条件を満たす間繰り返してくれる構文
インデックス番号の変数を定義 ⇒ {}の中に繰り返す変数を代入して実行 ⇒ 変数を変える ⇒ {}の中・・・
これだけみると、繰り返しの為に1要素ずつ取り出すという行為を 「必ず1つずつ取り出して」から、内容を実行するか「インデックス番号を指定して」から内容を実行するかの違いはありそう!!
例えるならば、後ろから順に取得したりとか、キリのいい数字で飛ばし飛ばし取得するのは「for文」しかできなさそうで、最初から順番に取得する場合は違いはあまりなさそうですね!!
⇒ ならば試しにやってみよう
##配列を用意する
数字だとイメージが湧きにくかったので、ある幸せそうな家族の「名前、年齢、身長、体重」のデータを用意しました
index | name | age | height | weight |
---|---|---|---|---|
0 | たろう | 43 | 178 | 83 |
1 | はなこ | 40 | 158 | 60 |
2 | なな | 8 | 124 | 25 |
const humans = [{
name:"たろう",
age:43,
height:178,
weight:83
},{
name:"はなこ",
age:40,
height:158,
weight:60
},{
name:"なな",
age:8,
height:124,
weight:23
}];
#コンソールにループさせた内容を出力してみる
##「for – of文 」で配列をループさせる
for (let human of humans){
console.log(human);
}
//以下出力内容
{name: "たろう", age: 43, height: 178, weight: 83}
{name: "はなこ", age: 40, height: 158, weight: 60}
{name: "なな", age: 8, height: 124, weight: 23}
中のオブジェクトが順番に出力される
配列の要素を1つずつ取り出して、Console.log実行
##「for」文で配列ループさせる
for (i = 0 ; i < humans.length ; i++){
console.log(humans[i]);
}
//以下出力内容
{name: "たろう", age: 43, height: 178, weight: 83}
{name: "はなこ", age: 40, height: 158, weight: 60}
{name: "なな", age: 8, height: 124, weight: 23}
これも中のオブジェクトが順番に出力される。
humans[0]を実行 ⇒ 「配列の1つ目の要素を出力してー」 ×3
###出力に違いってあるの?
「出力される内容」の違いはなかった。しかし、for内で定義している変数の対象が違うという発見!(あたりまえ)
☆「for-of文」は**「出力した内容」が変数として定義される**(⇒logのプロパティには、forの条件文の中で定義した変数を入れている)
☆「for 文」は変数は繰り返しの為の数値でインデックス番号として使用されている(⇒ iはループさせる為の数値、これがインデックス番号になるので、logのプロパティには、”配列の変数名"[i]となっている
)
イメージをふくらませると、「順番は最初から決まってるから、この順番(for-of文)通りに終わるまでよろしくねー」と「1番目!!やり方はこれだ!! 2番め!!やり方はさっきと一緒!! 3番目!!」みたいに順番を明確に指定してあげてるみたいかんじかな?
つまり、配列の中身を1番めからすべて出力する場合どちらを使っても問題ない。。。ハズ。。。。
(※実行スピードや、対応ブラウザ等は全く考慮してません)
#指定のプロパティのデータを取得してみる
出力された値は変わらなさそうなので、次は「for – of文 」と「for文」を使って、配列の中に入っているオブジェクトのプロパティのデータ(今回は名前)を取得して違いを比較してみます
##「for – of文」で配列の中のプロパティのデータを取得する
for (let human of humans){
console.log(human.name); //console.log(human["name"])でも可
}
//出力内容は以下
たろう
はなこ
なな
変数humansから1つ目の要素をとりだしたのをhumanに入れている間に{プロパティ名"name"のデータを出力する}を実行してね、それがhumansの要素の最後までおねがいね!
※それてしまうけれど、「for文の外で 変数"human"つかったらどうなるの?」と思って試した事は以下
//1. for終了後に、変数humanを呼び足してみたらどうなるか
for (let human of humans){
console.log(human.name);
}
console.log(human);
//出力内容
Uncaught ReferenceError: human is not defined
//humanなんて変数定義されてないよ
--------------------------------------------------------------------
//2 human.nameの出力の後にfor文内で、変数humanを出力したらどうなるの?
for (let human of humans){
console.log(human.name);
console.log(human);
}
//出力内容
たろう
{name: "たろう", age: 43, height: 178, weight: 83}
はなこ
{name: "はなこ", age: 40, height: 158, weight: 60}
なな
{name: "なな", age: 8, height: 124, weight: 23}
↓私の思考の遍歴です。
~~なるほど。代入演算子"="が使われていないから、for-of文内ではないと、for文内で定義した変数名はつかえないってことね!!ちょっと頭がすっきりしたきがする ~~
この部分は「代入演算子」の問題ではなく、変数のスコープと言われるものらしいです。
ブロック内{ }
で宣言されたletは、ブロック外では使えないという決まりです。
複雑になればなるほどわからなくなっていくので、できる限りグローバル変数は使わない方がいいらしいです!
(そしてこんなふうに考えてた自分もいるっていうのを残す為、あえて消さないでおきます(´・ω・`))
##「for文」で配列の中のプロパティのデータを取得する
for (i = 0 ; i < humans.length ; i++){
console.log(humans[i].name); //console.log(humans[i]["name"])でもok
}
//出力内容は以下
たろう
はなこ
なな
変数humansの[i]番目の要素のnameプロパティにはいっているデータを出力してね。
そして今回は代入演算子"="がつかわれているから、forの外で"変数i"出力したら「3」が返ってくるはず!!
console.log(i);
//出力内容
3
キタ━━━━(゚∀゚)━━━━!!予想的中!!
代入演算子がないと、文の外ではその変数つかえなくなるってことね!!
すごーくすっきりした!!
だからお前変数のスコーp(ry
ちなみに i = 0 は、 var i = 0 のことで、varはグローバルスコープになるから、「3」の結果が出力されたって事みたい!
この件再度フィードバックもらえました!!
JSだと、 i = 0
の様に let
、var
を省略すると、「グローバル変数」になってしまうそうです.
実践では、{ }
の内部にグローバル変数を持たせない方がいいとの事なので
ES6をつかっているのであればfor ( let i = O ; ~
letを省略をせずに書いた方がよさそうです。
#forで出力した内容で関数をつかってみる
それならついでに、forで出力した内容を使って関数でかいてみる!
今回は、身長と体重からBMIを出力する関数(に使用する引き数を)「for-of」と「for」を使って比較をしてみる
const bmicast = (height, weight) => { //引数(height , weight)を受け取る関数作成
const cast = weight / (height ** 2); //BMI = 体重 / (身長 * 身長)
const bmi = Math.floor(cast); //小数点以下をきりすて
console.log(bmi); //BMI出力してね
}
for(let human of humans){
let height = human.height / 100; //身長をメートルに換算
let weight = human.weight;
bmicast(height, weight); //関数呼び出し
}
//出力内容
26
24
14
##forで出力した内容を使ってみる
const bmicast = (height, weight) => {
const cast = weight /( height ** 2 );
const bmi = Math.floor(cast);
console.log(bmi);
}
for(i = 0 ; i < humans.length ; i++){
let height = humans[i].height / 100; //メートルに換算
let weight = humans[i].weight;
bmicast(height, weight); //関数呼び出し
}
//出力内容
26
24
14
うんこれも出力内容かわらないね。。
⇒それなら、出力したBMIを配列に追加したらどうなるだろう!!
#出力したBMIを配列に追加をする
##「for文」をつかってオブジェクトにプロパティを追加する
少し変数名をさわっていますが、繰り返し処理の最後に、配列操作しています
const addBMI = (height, weight) => { // addBMIという関数を定義
const cast = weight /( height ** 2 ); // BMIの計算
const bmi = Math.floor(cast); // 少数点以下を切り捨て
humans[i].bmi = bmi; // 配列humansの i番目の一番うしろにプロパティ名bmiとして、変数bmiを代入
}
for(i = 0 ; i < humans.length ; i++){
let height = humans[i].height / 100; //メートルに換算
let weight = humans[i].weight;
addBMI(height, weight);
}
console.log(humans); // 変数humasを出力
//以下出力内容
[{
name: "たろう", age: 43, height: 178, weight: 83, bmi: 26
},{
name: "はなこ", age: 40, height: 158, weight: 60, bmi: 24
},{
name: "なな", age: 8, height: 124, weight: 23, bmi: 14
}]
オブジェクトの最後に計算されたBMIが追加されました!!
[i]番目の配列に、プロパティ名bmiのプロパティを追加するという処理だけなので簡単です!!
⇒ この書き方は関数のスコープがしっかりしてないから良くないらしい。。。関数の変数内は関数内でおさめた方が今後の為だそうです!
##「for文」 レビュー後
const addBmi = (height, weight, human) => {
const cast = weight / (height ** 2); //bmi計算
const bmi = Math.floor(cast) //切り捨て
human.bmi = bmi //オブジェクトの最後にbmiを追加
}
for (i=0 ; i < humans.length ; i++){
const height = humans[i].height / 100 ; //メートルに換算
addBmi(height, humans[i].weight, humans[i]); //関数呼び出し
}
console.log(humans);
まずはじめに
let weight = humans[i].weight;
はいらないよー!
引数にあてはめる時にそのまま、weightプロパティを取得できるという事でした!!
また引数にhumans[i]
(配列のhumansのi番目のオブジェクト)をわたして
関数内で、humans[i].bmi = bmi
と同じ状況をつくってあげています!
こうすることで、グローバル変数をつかわなくて済むとの事です。
##「for - of文」をつかってオブジェクトにプロパティを追加する
これで書き換えようとおもった時に、全然いい方法がおもいつかなかったので、社内エンジニアの方にたすけてもらいました。
それがこちら
const reBmi = (height, weight) => { //bmiをリターンする関数を定義
const cast = weight / (height ** 2); //bmi計算
const bmi = Math.floor(cast); //きりすて
return bmi;
}
for(let human of humans){
let height = humam.height / 100; //メートル換算
human.bmi = reBmi(height, weight); // bmiをプロパティ名、reBmi関数のリターンをデータとして
} // 取り出したオブジェクトに追加する
console.log(humans);
なるほどこういう風に書けばいいのか。。!!
#最後に
最初は「for – of文 」と「for文」のちがいがよくわからずに、違いがあるのかな?と思って始めたものが、気づいたら関数の取扱いの方で迷子になってました(´・ω・`)
「for – of文 」と「for文」はお好みでどーぞだと思います!
関数は値として扱える
関数は何をいれて、何を出すのかを意識して考える
関数は書き方を覚えるしかない気がしてきました。
そして、配列操作はもう一回勉強しなおさないと。。。!!