目次
サンプル・プログラム
4年に一度のうるう年
ifElse1.html
19: <script>
20: /**
21: * 判定と画面表示
22: */
23: function ifElse1() {
24: //変数宣言
25: const year = Number.parseInt(document.getElementById('year').value, 10); //西暦年
26: if (!Number.isInteger(year)) {
27: document.getElementById('ret').textContent = '整数を入力してください';
28: return;
29: }
30:
31: //うるう年判定(間違いがある)
32: let ret = '平年';
33: if (year % 4 === 0) {
34: ret = 'うるう年';
35: }
36:
37: //結果を表示する
38: document.getElementById('ret').textContent = ret;
39: }
40: </script>
if文は if (条件式) のように用い、条件式が真(truthy)と評価されたとき、それに続くブロック {...} で囲まれた処理を実行する。
このプログラムでは、まず、判定結果を格納する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: const year = Number.parseInt(document.getElementById('year').value, 10); //西暦年
26: if (!Number.isInteger(year)) {
27: document.getElementById('ret').textContent = '整数を入力してください';
28: return;
29: }
30: let ret;
31:
32: //うるう年判定(正確なもの)
33: if (year % 400 === 0) {
34: ret = 'うるう年';
35: } else if (year % 100 === 0) {
36: ret = '平年';
37: } else if (year % 4 === 0) {
38: ret = 'うるう年';
39: } else {
40: ret = '平年';
41: }
42:
43: //結果を表示する
44: document.getElementById('ret').textContent = ret;
45: }
46: </script>
if~else文は次のようにして使う。
if (式1) {
ブロック1
} else if (式2) {
ブロック2
} else {
ブロック3
}
- もし式1がtrueならブロック1を実行する。
- 式1がfalseであり、式2がtrueならブロック2を実行する
- 式1と式2がともにfalseならブロック3を実行する。
ここで if~else文 の順序に気をつけよう。
if (year % 4) を先頭に持ってくると、西暦2100年(令和82年)もうるう年になってしまう。
まず、グレゴリオ暦の定義を次のように並べ替える必要がある。
- もし西暦年を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 についても同様のことが言える。
ブラウザで動く通常のスクリプトでは、window がグローバルオブジェクトである。
ここでは window.x というプロパティに文字列 'x' を代入し、その動作を見ている(2)。グローバルオブジェクトのプロパティなので、関数内でも window.x として参照でき(6)、この例では window. を省略した x でも参照できる(7)。ただし、同名のローカル変数があれば、識別子 x は内側のスコープにある変数を指す。なお、トップレベルで let 宣言した変数はグローバルスコープを持つが、window のプロパティにはならない。
コラム:レキシカルスコープ
let や const をブロック内で宣言するとブロックスコープになり、関数内で宣言した変数はその関数の外から参照できない。通常のスクリプトで関数やブロックの外側に宣言した変数はグローバルスコープを持つ。JavaScriptモジュールには、これとは別にモジュールスコープがある。
では、次のように関数内で関数を宣言した場合、変数 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>
一方、外側の関数で宣言した変数 a は hogeSub からも参照できる(3)。内側のスコープから外側のスコープをたどって変数を探すためである。
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で閏年(うるう年)かどうか判定する:ぱふぅ家のホームページ
- if...else:MDN Web Docs
- ブロック文:MDN Web Docs
- let:MDN Web Docs
- 自動セミコロン挿入:MDN Web Docs

制御を活用することで、Excel関数だけでは実現できなかった、さまざまな処理が実現できるようになる。