
目次
サンプル・プログラム
4年に一度のうるう年
ifElse1.html
19: <script>
20: /**
21: * 判定と画面表示
22: */
23: function ifElse1() {
24: //変数宣言
25: let year = document.getElementById('year').value; //西暦年
26: year = parseInt(year, 10); //10進整数化
27:
28: //うるう年判定(間違いがある)
29: let ret = '平年'
30: if (year % 4 === 0) {
31: ret = 'うるう年';
32: }
33:
34: //結果を表示する
35: document.getElementById('ret').innerHTML = ret;
36: }
37: </script>
if文は if (式) のように用い、式が true のとき、それに続くブロック {...} で囲まれた中身)を実行する。

このプログラムでは、まず、判定結果を格納するString型変数 ret に '平年' を代入しておく。
次に、if文を使い、西暦年が格納されたNumber型変数 year の4の剰余を計算し、計算結果が0でない(4で割り切れる)場合は、変数 ret に 'うるう年' を代入する。
最後に、変数 ret の内容を表示する。
正確なうるう年判定
現在の西暦であるグレゴリオ暦で、うるう年は次のように定義されている。
- 4で割り切れる年はうるう年である。
- ただし、100で割り切れる年は平年である。
- ただし、400で割り切れる年はうるう年である。
ifElse2.html
19: <script>
20: /**
21: * 判定と画面表示
22: */
23: function ifElse2() {
24: //変数宣言
25: let year = document.getElementById('year').value; //西暦年
26: year = parseInt(year, 10); //10進整数化
27: let ret;
28:
29: //うるう年判定(正確なもの)
30: if (year % 400 === 0) {
31: ret = 'うるう年';
32: } else if (year % 100 === 0) {
33: ret = '平年';
34: } else if (year % 4 === 0) {
35: ret = 'うるう年';
36: } else {
37: ret = '平年';
38: }
39:
40: //結果を表示する
41: document.getElementById('ret').innerHTML = ret;
42: }
43: </script>
if~else文は次のようにして使う。
if (式1) {
ブロック1
} else if (式2) {
ブロック2
} else (式3) {
ブロック3
}
- もし式1がtrueならブロック1を実行する。
- 式1がfalseであり、式2がtrueならブロック2を実行する
- 式1,式2がともにfalseであり、式3がtrueならブロック3を実行する。

ここで if~else文 の順序に気をつけよう。
if (year % 4) を先頭に持ってくると、西暦2100年もうるう年になってしまう。
まず、グレゴリオ暦の定義を次のように並べ替える必要がある。
- もし西暦年を400で割り切れれば、うるう年である。
- そうでなければ、西暦年を100で割り切れれば、平年である。
- そうでなければ、西暦年を4で割り切れれば、うるう年である。
- そうでなければ、平年である

式とブロック文
ret = 'うるう年';複数の文をまとめてブレース {...} で区切ったものをブロック文と呼ぶ。
if~else文 は、条件によって、それに続くブロック文を実行するか否かを制御していると言える。

なお、"ifElse2.html" のようにブロック文の中の式が1つしかない場合、ブレース {...} を省略して次のように書くこともできる。
if (year % 400 === 0) ret = 'うるう年';しかし、あとでプログラムを修正・改変するときに式が増えるといけないので、最初からブレース {...} で囲んでおいた方がメンテナンス性は高まる。
else if (year % 100 === 0) ret = '平年';
else if (year % 4 === 0) ret = 'うるう年';
else ret = '平年';
実用で使うことはあまりないが、制御を伴わないブロック文を使って、この作用を確認しておこう。
let1.html
19: <script>
20: //ページのロード時に実行
21: window.onload = function() {
22: let i = 1; //変数宣言・その1
23: {
24: let i = 2; //変数宣言・その2
25: {
26: let i = 3; //変数宣言・その3
27: document.getElementById('let1').innerHTML = i;
28: }
29: document.getElementById('let2').innerHTML = i;
30: }
31: document.getElementById('let3').innerHTML = i;
32: }
33: </script>
ブロック文の中でのみ必要となる作業用変数は、ブロック文の中で let 宣言 して使うといい。
練習問題:if〜else文
練習問題:セミコロンの役割
コラム:ExcelのIF関数、他言語の制御文、フロー図

うるう年の判定は、=IF(MOD(A1,400)=0,"うるう年",IF(MOD(A1,100)=0,"平年",IF(MOD(A1,4)=0,"うるう年","平年"))) とすることで実現はできるが、かなり読みにくくなってしまう。JavaScriptのように複数行にわたって書くことができるブロック文を制御する方が、読みやすい。
# うるう年判定(正確なもの)
if year % 400 == 0:
ret = 'うるう年'
elif year % 100 == 0:
ret = '平年'
elif year % 4 == 0:
ret = 'うるう年'
else:
ret = '平年'
他の言語も、おおむねJavaScriptと書き方は似ている。
「1.3 四則計算と変数」のコラムで紹介したプログラミング電卓付属の「プログラム・ライブラリ」にもフロー図が掲載されており、とても参考になった。
プログラミングに慣れるまでは、手間を惜しまず、自力でフロー図を書くことをお勧めする。

また、プログラミングに慣れた方でも、プログラムをフロー図にして見直すことで、条件式を正規化することなどして、制御の流れをシンプルにすることができる場合がある。
短くシンプルなプログラムほどバグが少ない。一度、自作プログラムを見直してみてはいかがだろうか。
コラム:グローバルスコープ、関数スコープ、ブロックスコープ
- グローバルスコープ
- 関数スコープ
- ブロックスコープ
scope1.html
52: <script>
53: // オブジェクトを描画後に実行する
54: let i = 1; // グローバルスコープ
55: document.getElementById('let1').innerHTML = i; // ‥‥(1)
56: window.x = 'x'; // グローバルスコープ
57: document.getElementById('let2').innerHTML = window.x; // ‥‥(2)
58: // 関数呼び出し
59: hoge();
60: document.getElementById('let8').innerHTML = i; // ‥‥(8)
61: // 関数定義
62: function hoge() {
63: let i = 2; // 関数スコープ
64: document.getElementById('let3').innerHTML = i; // ‥‥(3)
65: if (true) {
66: let i = 3; // ブロックスコープ
67: document.getElementById('let4').innerHTML = i; // ‥‥(4)
68: }
69: document.getElementById('let5').innerHTML = i; // ‥‥(5)
70: document.getElementById('let6').innerHTML = window.x; // ‥‥(6)
71: document.getElementById('let7').innerHTML = x; // ‥‥(7)
72: }
73: </script>
const についても同様のことが言える。

また、ブラウザで動く JavaScript の場合、windows オブジェクトのプロパティはグローバルスコープになる。
ここでは window.x というプロパティを用意し、そこに文字列 'x' を代入し、その動作を見ている(2)。グローバススコープなので、関数内でも参照することができる(6)。また、windows を省略することもできる(7)。ただし、もしブロックスコープで x が宣言されていた場合は、ブロックスコープが優先される。
コラム:レキシカルスコープ
前述の3つのスコープについて俯瞰してみると、ブロック内で宣言するとブロックスコープになり、関数内で宣言すると関数スコープになり、関数やクラスの外側で宣言するとグローバルスコープになることがわかる。

では、次のように関数内で関数を宣言した場合、変数 a, b のスコープはどうなるか。
scope2.html
46: <script>
47: // オブジェクトを描画後に実行する
48: // 関数定義
49: function hoge() {
50: let a = 1;
51: document.getElementById('let1').innerHTML = a; // ‥‥(1)
52: // 関数内で関数定義
53: function hogeSub() {
54: let b = 2;
55: document.getElementById('let2').innerHTML = b; // ‥‥(2)
56: document.getElementById('let3').innerHTML = a; // ‥‥(3)
57: }
58: // 関数実行
59: hogeSub();
60: // bは表示できるか?
61: document.getElementById('let4').innerHTML = b; // ‥‥(4)
62: }
63: // 関数実行
64: hoge();
65: </script>
一方、変数 b は関数内関数の範囲内でも参照可能で、b とはスコープが異なることがわかる。

JavaScriptでは、実行時に変数が出てきたら、まずいま実行しているブロックレベルで宣言されている同名変数を探す。そこになければ、より外側のブロックを探し、最終的にグローバルスコープを探す。途中で合致するものがあったら、その変数に対して処理を行う。これをスコープチェーンと呼ぶ。
レキシカルスコープは JavaScript を書いた時点に決定し、実行時に変化することはない。つまり、JavaScriptのスコープは静的で、動的ではない。
したがって、サンプル・プログラム "scope3.html" の結果は "foo" ではなく "global" となる。
scope3.html
42: <script>
43: // オブジェクトを描画後に実行する
44: let x = 'global';
45:
46: // 関数定義
47: function hoge() {
48: document.getElementById('let1').innerHTML = x; // ‥‥(1)
49: }
50:
51: // 関数定義
52: function foo() {
53: let x = 'foo';
54: // 関数実行
55: hoge();
56: }
57:
58: // 関数実行
59: foo();
60: </script>
参考サイト
- オリンピック・イヤーは閏年(うるう年)か?:ぱふぅ家のホームページ
- PHPで閏年(うるう年)かどうか判定する:ぱふぅ家のホームページ
制御を活用することで、Excel関数だけでは実現できなかった、さまざまな処理が実現できるようになる。