034.3 レッスン 1
Certificate: |
Web開発の要点 |
---|---|
Version: |
1.0 |
Topic: |
034 JavaScriptプログラミング |
Objective: |
034.3 JavaScriptの制御構造と関数 |
Lesson: |
1 of 2 |
はじめに
他のプログラミング言語と同様に、JavaScriptのコードは、命令インタプリタに順番に何をすべきかを指示するステートメントの集合体です。しかし、すべてのステートメントが一度だけ実行されるべきであるとか、まったく実行されるべきではないということではありません。ほとんどのステートメントは、特定の条件が満たされたときにのみ実行されます。独立したイベントによって非同期的に起動されるスクリプトであっても、実行すべきコードの適切な部分を見つけるために、多くの制御変数をチェックする必要があります。
Ifステートメント
最も単純な制御構造は、 if
命令であり、指定された条件が真であれば、直後の文を実行します。JavaScriptでは、評価された値が0でなければ条件が真であるとみなします。 if
の後の括弧内のもの(スペースは無視されます)はすべて条件として解釈されます。次の例では、リテラルの数字 1
が条件となります。
if ( 1 ) console.log("1 is always true");
この条件例では、数字の 1
が明示的に記述されているので、定数値として扱われ(スクリプトの実行中は同じ値になる)、条件式として使用すると常に真の値が得られます。また、 1
の代わりに true
という単語(二重引用符を使わない)を使うこともできます。これは、言語によって文字通りの真の値として扱われるためです。 console.log
命令は、ブラウザの コンソールウィンドウ にその引数を表示します。
Tip
|
ブラウザのコンソールには、JavaScriptの |
構文的には正しいのですが、定数式を条件に使うのはあまり便利ではありません。実際のアプリケーションでは、変数の真偽をテストしたい場合が多いでしょう。
let my_number = 3;
if ( my_number ) console.log("The value of my_number is", my_number, "and it yields true");
変数 my_number
に代入された値( 3
)は0ではないので、真を出力します。しかし、この例は一般的な使い方ではありません。なぜなら、ある数値がゼロに等しいかどうかをテストする必要はほとんどないからです。ある値と別の値を比較して、その結果が真であるかどうかをテストする方がずっと一般的です。
let my_number = 3;
if ( my_number == 3 ) console.log("The value of my_number is", my_number, "indeed");
比較演算子のダブルイコールが使われているのは、シングルイコール演算子がすでに代入演算子として定義されているからです。演算子の両側にある値を オペランド と呼びます。オペランドの順序は問題ではなく、値を返すあらゆる式がオペランドになります。ここでは、他の利用可能な比較演算子のリストを示します。
value1 == value2
-
True if
value1
is equal tovalue2
. value1 != value2
-
True if
value1
is not equal tovalue2
. value1 < value2
-
True if
value1
is less thanvalue2
. value1 > value2
-
True if
value1
is greater thanvalue2
. value1 <= value2
-
True if
value1
is less than or equal tovalue2
. value1 >= value2
-
True if
value1
is grater than or equal tovalue2
.
通常、演算子の左側のオペランドが文字列で、右側のオペランドが数値であるかどうかは、JavaScriptが表現を意味のある比較に変換できれば問題ありません。つまり、文字 1
を含む文字列は、数値変数と比較された場合、数値 1 として扱われます。両方のオペランドが全く同じ型と値の場合にのみ式が真をもたらすようにするには、 ==
の代わりに厳格な同一性演算子 ===
を使用する必要があります。同様に、厳格な非同一性演算子 !==
は、第一演算子が第二演算子と全く同じ型と値でない場合に真と評価されます。
オプションとして、 if
制御構造は、式がfalseと評価されたときに代替ステートメントを実行することができます。
let my_number = 4;
if ( my_number == 3 ) console.log("The value of my_number is 3");
else console.log("The value of my_number is not 3");
また、 else
命令は if
命令の直後に記述します。ここまでは、条件が満たされたときに1つのステートメントだけを実行しています。複数のステートメントを実行するには、中括弧で囲む必要があります。
let my_number = 4;
if ( my_number == 3 )
{
console.log("The value of my_number is 3");
console.log("and this is the second statement in the block");
}
else
{
console.log("The value of my_number is not 3");
console.log("and this is the second statement in the block");
}
1つまたは複数のステートメントを中括弧で囲んだものを ブロックステートメント と呼びます。実行する命令が1つしかない場合でも、スクリプト全体でコーディングスタイルを統一するために、ブロックステートメントを使用するのが一般的です。また、JavaScriptでは、中括弧やすべてのステートメントが別の行にある必要はありませんが、そうすることで可読性が向上し、コードのメンテナンスが容易になります。
制御構造は互いに入れ子にすることができますが、各ブロック文の開始中括弧と終了中括弧を混在させないことが重要です。
let my_number = 4;
if ( my_number > 0 )
{
console.log("The value of my_number is positive");
if ( my_number % 2 == 0 )
{
console.log("and it is an even number");
}
else
{
console.log("and it is an odd number");
}
} // end of if ( my_number > 0 )
else
{
console.log("The value of my_number is less than or equal to 0");
console.log("and I decided to ignore it");
}
if
命令で評価される式は、単純な比較だけでなく、より複雑なものにすることができます。前述の例では、入れ子になった if
の括弧内に算術式 my_number % 2
が採用されています。 %
演算子は、左の数値を右の数値で割った余りを返します。 %
のような算術演算子は、 ==
のような比較演算子よりも優先されるので、比較には算術式の結果を左オペランドとして使用します。
多くの場合、入れ子になった条件構造は、 論理演算子 を使って一つの構造にまとめることができます。例えば、正の偶数にしか興味がない場合は、単一の if
構造を使用することができます。
let my_number = 4;
if ( my_number > 0 && my_number % 2 == 0 )
{
console.log("The value of my_number is positive");
console.log("and it is an even number");
}
else
{
console.log("The value of my_number either 0, negative");
console.log("or it is a negative number");
}
評価式の中のダブルアンパサンド演算子 &&
は、論理演算子の AND(かつ) です。左の式と右の式が真と評価された場合にのみ真と評価されます。正または偶数の数値にマッチさせたい場合は、代わりに論理演算子 OR(または) を表す ||
演算子を使用する必要があります。
let my_number = -4;
if ( my_number > 0 || my_number % 2 == 0 )
{
console.log("The value of my_number is positive");
console.log("or it is a even negative number");
}
この例では、負の奇数のみが複合式で課された基準にマッチしません。逆の意図、つまり負の奇数のみにマッチさせたい場合は、論理的な NOT 演算子 !
を式の先頭に追加します。
let my_number = -5;
if ( ! ( my_number > 0 || my_number % 2 == 0 ) )
{
console.log("The value of my_number is an odd negative number");
}
複合式の中に括弧を加えることで、括弧で囲まれた式が最初に評価されるようになります。この括弧がないと、NOT演算子は my_number > 0
にのみ適用され、その後にOR式が評価されることになります。 &&
と ||
演算子は2つのオペランドを必要とするため、 二項 論理演算子として知られています。 !
は、オペランドを1つだけ必要とするため、 単項 論理演算子として知られています。
スイッチ構造
if
構造は非常に汎用性が高く、プログラムのフローを制御するのに十分ですが、trueやfalse以外の結果を評価する必要がある場合には、 switch
制御構造の方が適しているかもしれません。例えば、リストから選択された項目ごとに個別のアクションを取りたい場合には、評価ごとに if
構造を記述する必要があります。
// Available languages: en (English), es (Spanish), pt (Portuguese)
let language = "pt";
// Variable to register whether the language was found in the list
let found = 0;
if ( language == "en" )
{
found = 1;
console.log("English");
}
if ( found == 0 && language == "es" )
{
found = 1;
console.log("Spanish");
}
if ( found == 0 && language == "pt" )
{
found = 1;
console.log("Portuguese");
}
if ( found == 0 )
{
console.log(language, " is unknown to me");
}
この例では、すべての if
構造体が補助変数 found
を使用して、一致が発生したかどうかを確認します。このようなケースでは、 switch
構造体が同じタスクを実行しますが、より簡潔な方法になっています。
switch ( language )
{
case "en":
console.log("English");
break;
case "es":
console.log("Spanish");
break;
case "pt":
console.log("Portuguese");
break;
default:
console.log(language, " not found");
}
入れ子になった各 case
を 節 と呼びます。ある節が評価された式にマッチすると、コロンに続く文を break
文まで実行します。最後の節は break
文を必要とせず、他にマッチしないときのデフォルトの動作を設定するためによく使われます。例題で見たように、 switch
構造では補助変数は必要ありません。
Warning
|
|
複数の節が同じアクションを引き起こす場合、2つ以上の case
条件を組み合わせることができます。
switch ( language )
{
case "en":
case "en_US":
case "en_GB":
console.log("English");
break;
case "es":
console.log("Spanish");
break;
case "pt":
case "pt_BR":
console.log("Portuguese");
break;
default:
console.log(language, " not found");
}
ループ
これまでの例では、 if
や switch
といった構造体は、1つ以上の条件テストを通過した後に1回だけ実行する必要があるタスクに適していました。しかし、条件式が真であり続ける限り、タスクが繰り返し実行されなければならない、いわゆる ループ のような状況もあります。例えば、ある数字が素数かどうかを知りたい場合、その数字を1以上自分以下の整数で割ったときに、余りが0になるかどうかを調べる必要があります。もしそうなら、その数字は因数を持ち、素数ではありません(これは、素数を見つけるための厳密で効率的な方法ではありませんが、簡単な例としては有効です)。このような場合には、ループ制御構造の方が適しており、特に while
文が適しています。
// A naive prime number tester
// The number we want to evaluate
let candidate = 231;
// Auxiliary variable
let is_prime = true;
// The first factor to try
let factor = 2;
// Execute the block statement if factor is
// less than candidate and keep doing it
// while factor is less than candidate
while ( factor < candidate )
{
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next factor to try. Simply
// increment the current factor by one
factor++;
}
// Display the result in the console window.
// If candidate has no integer factor, then
// the auxiliary variable is_prime still true
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
while
命令に続くブロック文は、 factor < candidate
という条件が真である限り、繰り返し実行されます。また、 factor
変数を candidate
よりも小さい値で初期化する限り、少なくとも1回は実行されます。 while
構造の中に入れ子になっている if
構造は、 candidate
を factor
で割った余りがゼロかどうかを評価します。0であれば候補の数字は素数ではないので、ループを終了することができます。 break
文はループを終了させ、実行は while
ブロックの後の最初の命令にジャンプします。
while
文で使用する条件の結果は、ループごとに変化しなければならず、そうでなければブロック文は “永遠に” ループしてしまうことに注意してください。この例では、次に試したい割り算である factor
変数を増加させ、ループがどこかで終了することを保証しています。
この単純な素数検査器の実装は期待通りに動作します。しかし、2で割り切れない数は、他の偶数で割り切れないことがわかっています。したがって、別の if
命令を追加することで、偶数をスキップすることができます。
while ( factor < candidate )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
factor++;
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
// The next number that will divide the candidate
factor++;
}
continue
文は、 break
文と似ていますが、ループのこの繰り返しを終了するのではなく、ループの残りのブロックを無視して、新しい繰り返しを開始します。変数 factor
は continue
ステートメントの前に変更されていることに注意してください。そうでなければ、このループは次の反復で再び同じ結果になってしまいます。この例は単純すぎて、ループの一部をスキップしても実際のパフォーマンスは向上しませんが、冗長な命令をスキップすることは、効率的なアプリケーションを書く上で非常に重要です。
ループは非常によく使われるため、さまざまな種類が存在します。 for
ループは、ループのルールを1行で定義できるので、連続した値を繰り返し処理するのに適しています。
for ( let factor = 2; factor < candidate; factor++ )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
}
この例では、前の while
の例とまったく同じ結果が得られますが、その親要素の表現には、セミコロンで区切られた3つの部分があります。初期化( let factor = 2
)、ループの条件( factor < candidate
)、各ループの繰り返しの最後に評価される最終式( factor++
)です。また、 continue
と break
は、 for
ループにも適用されます。括弧内の最終式( factor++
)は、 continue
文の後に評価されるので、ブロック文の中({と}の間)に入れてはいけません。そうしないと、次の繰り返しの前に2回インクリメントされてしまいます。
JavaScriptには、配列のようなオブジェクトを扱うための特別なタイプの for
ループがあります。例えば、候補となる変数を1つだけではなく、配列でチェックすることができます。
// A naive prime number tester
// The array of numbers we want to evaluate
let candidates = [111, 139, 293, 327];
// Evaluates every candidate in the array
for (candidate of candidates)
{
// Auxiliary variable
let is_prime = true;
for ( let factor = 2; factor < candidate; factor++ )
{
// Skip even factors bigger than two
if ( factor > 2 && factor % 2 == 0 )
{
continue;
}
if ( candidate % factor == 0 )
{
// The remainder is zero, so the candidate is not prime
is_prime = false;
break;
}
}
// Display the result in the console window
if ( is_prime )
{
console.log(candidate, "is prime");
}
else
{
console.log(candidate, "is not prime");
}
}
for (candidate of candidates)
文では、配列 candidates
の1つの要素を変数 candidate
に代入して、ブロック文で使用し、配列の各要素に対して処理を繰り返しています。 for
ループで定義されているので、 candidate
を個別に宣言する必要はありません。最後に、前の例の同じコードをこの新しいブロック文の中に入れ子にして、今回は配列の各候補をテストします。
演習
-
変数
my_var
のどの値が、my_var > 0 && my_var < 9
という条件に合致するでしょうか? -
my_var
変数のどの値が、my_var > 0 || my_var < 9
という条件に合致するでしょうか? -
次の
while
ループは、このブロック文を何回実行しますか?let i = 0; while ( 1 ) { if ( i == 10 ) { continue; } i++; }
発展演習
-
等価比較演算子
==
の代わりに、等価代入演算子=
を使うとどうなりますか? -
if
制御構造を使って、通常の等値比較ではtrueを返し、厳密な等値比較ではtrueを返さないコードを書いてください。 -
ループ条件に単項NOT論理演算子を用いて、以下の
for
文を書き換えてください。条件の結果は同じである必要があります。for ( let factor = 2; factor < candidate; factor++ )
-
このレッスンの例に基づいて、与えられた数のすべての整数の因数を表示するループ制御構造を書いてください。
まとめ
このレッスンでは、JavaScriptのコードで制御構造を使用する方法について説明しました。条件分岐構造やループ構造は、あらゆるプログラミングパラダイムに欠かせない要素であり、JavaScriptによるWeb開発も例外ではありません。このレッスンでは、以下の概念と手順を説明しました。
-
if
文と比較演算子 -
switch
文をcase
、default
、break
とともに使用する方法 -
通常の比較と厳密な比較の違い
-
ループ制御構造。
while
とfor
演習の回答
-
変数
my_var
のどの値が、my_var > 0 && my_var < 9
という条件に合致するでしょうか?0より大きくて9より小さい数字のみ。論理演算子の
&&
(AND)は、両方の比較が一致する必要があります。 -
変数
my_var
のどの値が、my_var > 0 || my_var < 9
という条件に合致するでしょうか?論理演算子
||
(OR)を使用すると、任意の数字が0より大きいか9より小さいかのどちらかになるため、任意の数字がマッチします。 -
次の
while
ループは、このブロック文を何回実行しますか?let i = 0; while ( 1 ) { if ( i == 10 ) { continue; } i++; }
The block statement will repeat indefinitely, as no stop condition was supplied.
発展演習の回答
-
等価比較演算子
==
の代わりに、等価代入演算子=
を使うとどうなりますか?演算子の右辺の値が左辺の変数に代入され、その結果が比較に渡されますが、これは望ましい動作ではないかもしれません。
-
if
制御構造を使って、通常の等値比較ではtrueを返し、厳密な等値比較ではtrueを返さないコードを書いてください。let a = "1"; let b = 1; if ( a == b ) { console.log("An ordinary comparison will match."); } if ( a === b ) { console.log("A strict comparison will not match."); }
-
ループ条件に単項NOT論理演算子を用いて、以下の
for
文を書き換えてください。条件の結果は同じである必要があります。for ( let factor = 2; factor < candidate; factor++ )
答え:
for ( let factor = 2; ! (factor >= candidate); factor++ )
-
このレッスンの例に基づいて、与えられた数のすべての整数の因数を表示するループ制御構造を書いてください。
for ( let factor = 2; factor <= my_number; factor++ ) { if ( my_number % factor == 0 ) { console.log(factor, " is an integer factor of ", my_number); } }