Lesson 1
JavaScriptの基礎
目次
Lesson 1
Chapter 1
内部処理
代入
代入とは、何等かの値を後から取り出せるようにメモリに記憶、保存しておく処理です。コンピュータプログラムによる処理の基本になります。代入を実際に試すためにウェブブラウザの開発者ツールを使います。Chromeを起動してF12キーを押し、次の画面を出してください。
画面右側の「>」と表示されている所をクリックしてから次のように入力してEnterキーを押してください。
x = 1
すると、次のように表示されます。
これで、「x」という名前がついたメモリに「1」という値が保存されました。値を調べるには「x」と入力してEnterキーを押してください。
先ほど代入した「1」が表示されます。1以外の値やx以外の名前でも試してみてください。
算術演算
算術演算とは、本来は広い意味合いがありますが、ここではまず四則演算について学習します。次に四則演算以外のいくつかの計算について学習します。
四則演算
加算、減算、乗算、除算の4種類の計算を四則演算と呼びます。それぞれの計算に、計算の種類を表す記号があります。これを「演算子」と呼びます。 加算の場合は「+」。減算の場合は「-」。乗算の場合は「*」。除算の場合は「/」です。ウェブブラウザの開発者ツールで、実際に試してみます。
剰余演算
整数の除算をした時の余りを求める計算を剰余演算と呼びます。剰余演算を表す演算子は「%」です。実際に試してみます。
例では10を3で割った余りと、100を60で割った余りを求めています。
べき乗演算
べき乗の計算もできます。べき乗を表す演算子は「**」です。実際に試してみます。
例では2の4乗と、10の3乗を求めています。
比較演算
二つの値を比較して、その関係を調べるには比較演算を行います。等しい、大きい、小さいといった関係が調べられます。比較演算の結果はこれまでの算術演算とは異なり、trueかfalseという二つの値のどちらかが出力されます。等しいかどうかを調べる演算をした結果がtrueである場合は二つの値は等しく、falseの場合は等しくありません。true、falseという種類の値の事を真偽値といいます。
等値演算
二つの値を比較して等しいかどうかを調べます。調べるには同値演算子(===)、非同値演算子(!==)を使います。実際に試してみます。
例では1と2を同値演算子と非同値演算子を使って比較しています。比較した結果としてtrueとfalseが出力されています。それぞれの結果と意味は次の通りです。
- 1===1 1と1が等しいかを調べています。値が等しい時、trueが出力されます。
- 1===2 1と2が等しいかを調べています。値が等しくない時、falseが出力されます。
- 1!==1 1と1が等しくないかを調べています。値が等しい時、falseが出力されます。
- 1!==2 1と2が等しくないかを調べています。値が等しくない時、trueが出力されます。
「同値ですか?」という問い(演算子)に対して「Yes」であれば「true」、「No」であれば「false」が出力されます。「非同値ですか?」という問い(演算子)に対して「Yes」であれば「true」、「No」であれば「false」が出力されます。
関係演算
二つの値を比較してどちらが大きいか、小さいかを調べます。調べるには小なり演算子(<)、大なり演算子(>)、小なりイコール演算子(<=)、大なりイコール演算子(>=)を使います。実際に試してみます。
例では小なり演算子と小なりイコール演算子を使って比較しています。それぞれの結果と意味は次の通りです。
- 1>1 1が1より大きいかを調べています。演算子の左側が右側より大きくない時、falseが出力されます。
- 2>1 2が1より大きいかを調べています。演算子の左側が右側より大きい時、trueが出力されます。
- 1>=1 1が1以上かを調べています。値が等しい時、trueが出力されます。
- 1>=2 1が2以上かを調べています。演算子の左側が右側以上ではない時、falseが出力されます。
次の例では大なり演算子と大なりイコール演算子を使って比較しています。それぞれの結果と意味は次の通りです。
- 1<1 1が1より大きいかを調べています。演算子の右側が左側より大きくない時、falseが出力されます。
- 1<2 2が1より大きいかを調べています。演算子の右側が左側より大きい時、trueが出力されます。
- 1<=1 1が1以上かを調べています。値が等しい時、trueが出力されます。
- 2<=1 1が2以上かを調べています。演算子の右側が左側以上ではない時、falseが出力されます。
「演算子の左側が右側より大きいですか?」という問い(演算子)に対して「Yes」であれば「true」、「No」であれば「false」が出力されます。「演算子の右側が左側以上ですか?」という問い(演算子)に対して「Yes」であれば「true」、「No」であれば「false」が出力されます。
論理演算
比較演算の結果として真偽値が得られる事を前のチャプターで説明しました。この真偽値を使った計算が論理演算です。論理演算には論理積(&&)と論理和(||)があり、真偽値との組み合わせで次のような結果となります。
論理積は「右側がtrueであり、なおかつ左側がtrueであるなら、trueである」と読み替えられます。論理和は「右側がtrueであるか、もしくは左側がtrueであるなら、trueである」と読み替えられます。
ビット演算
注意:内部表現と二進数を理解しないと理解が困難である為、教育コースの学習順としては後ろに配置すること。
ビット演算とは、JavaScript内部で値を表すバイナリ値を直接操作する計算です。JavaScriptの数値は内部的には64ビットで表現されており、これを操作する計算になります。
ビットシフト演算
ビットシフト演算とは、数値を表す64ビットのビット列を下位ビットから上位ビットに向かって、もしくは上位ビットから下位ビットに向かってずらす(シフトする)操作のことです。下位ビットから上位ビットに向かってシフトすることを左シフト。上位ビットから下位ビットに向かってシフトすることを右シフトと言います。左シフトの演算子は<<。右シフトの演算子は>>になります。実際に試してみます。
左から見て最初の値が操作の対象となる値です。次がビットシフトの方向です。最後が何ビットシフトするかになります。1を1ビット左シフトすると2になります。2を1ビット右シフトすると1になります。nビットシフトすると実質的に2**n倍・1/(2**n)にするのと同じになります。実際に試してみてください。
バイナリービット演算
バイナリービット演算とはJavaScript内部で値を表すバイナリ値を直接操作する計算です。0をfalse、1をtrueに置き換えた各ビットの桁毎に行う論理演算と考えられます。ビット論理積(&)、ビット論理和(|)、ビット排他的論理和(^)の3種類の演算があります。実際に試してみます。
10進数の表現では直観的に理解が難しいかもしれません。二進数を使って表現してみます。
10進数の7は二進数表現では0b111となり、0b100と0b011の各桁で論理和を取った結果であることがわかります。0b100と0b011の各桁で論理積を取ると0となります。排他的論理和とは入力が0と1、もしくは1と0の場合のみ1となる論理演算で、1と1もしくは0と0の場合は0となります。0b1100と0b1010の排他的論理和を取ると、0b0110となり、これは10進数の6となります。
10進数の数を二進数で表現した時にどのようになるかを調べる方法を示します。
(14).toString(2)
この場合、14が調べたい10進数の数です。2は二進数の2を表します。実際に試すとこのようになります。
その他の演算
typeof
注意:値の型について理解しないと理解が困難である為、教育コースの学習順としては後ろに配置すること。
typeof演算子は与えられた値の型を表す文字列を取得する演算子です。関数の引数などで、受け取った変数にどのような型の値が入っているのかを調べる時に使用できます。判断できるのはプリミティブ型のみで以下の型に対応しています。
- 数値
- 文字列
- 論理値
- 関数
- Undefined
- Null
- BigInt
- シンボル
- その他
実際に試すとこのようになります。
instanceof
注意:クラスについて理解しないと理解が困難である為、教育コースの学習順としては後ろに配置すること。
instanceof演算子はオブジェクト型の値について、何のクラスのインスタンスであるのかを調べる演算子です。結果としてtrueかfalseを返します。使うときには次のように記述します。
instanceof
では、実際に試してみます。
xにDateクラスのインスタンスを設定し、instanceofでDateクラスのインスタンスであるかどうかを調べるとtrueが返ります。yに数値プリミティブ型の値を設定してinstanceofでDateクラスのインスタンスであるかどうかを調べるとfalseが返ります。
演算子の優先順位
これまで様々な演算子について説明してきました。複数の演算子を組み合わせた計算を行う場合には決まった実行順序(演算子の優先順位)があります。演算子の優先順位を考慮して式を組み立てないと、期待と異なる結果になる場合があります。演算子の優先順位を理解して、期待通りに計算が行われる式を組み立てるようにします。以下に演算子の種類を優先順位順に並べたものを示します。優先順位が高い(先に計算される)ものから順に示しています。
- グループ化
- メンバーへのアクセス
- 計算値によるメンバーへのアクセス
- new (引数リスト付き)
- 関数呼び出し
- オプショナルチェイニング
- new (引数リストなし)
- 後置インクリメント
- 後置デクリメント
- 論理 NOT
- ビットごとの NOT
- 単項 +
- 単項 -
- 前置インクリメント
- 前置デクリメント
- typeof
- void
- delete
- await
- べき乗
- 乗算
- 除算
- 剰余
- 加算
- 減算
- 左ビットシフト
- 右ビットシフト
- 符号なし右ビットシフト
- 小なり
- 小なりイコール
- 大なり
- 大なりイコール
- in
- instanceof
- 等価
- 不等価
- 厳密等価
- 厳密不等価
- ビット単位論理和
- ビット単位排他的論理和
- ビット単位論理積
- 論理和
- 論理積
- Null 合体
- 条件
- 代入
- yield
- yield*
- カンマ / シーケンス
最も優先順位が高い演算子として「グループ化」があります。これは()によって先に計算したい部分を指定することで、計算順を先にする演算子です。実際に試してみます。
1 + 2 * 3という式では+よりも*の方が優先順位が高い為、2 * 3が先に計算されその結果に1を加える為に結果が7となります。しかし、カッコを使い、(1 + 2)を先に計算すると結果は9に変わります。自分が意図した計算順を明示する為に積極的にカッコを使うと、式の可読性が向上します。
Math組み込みオブジェクト
Mathは数学的な計算を行う為に必要なプロパティや関数をまとめたオブジェクトです。円周率の定数PIや自然対数の底であるE、三角関数や小数点以下切り捨てなどの関数があります。コンソールからMathと入力すると、プロパティと関数の一覧が取得できます。
- E: 2.718281828459045
- LN2: 0.6931471805599453
- LN10: 2.302585092994046
- LOG2E: 1.4426950408889634
- LOG10E: 0.4342944819032518
- PI: 3.141592653589793
- SQRT1_2: 0.7071067811865476
- SQRT2: 1.4142135623730951
- abs: ƒ abs()
- acos: ƒ acos()
- acosh: ƒ acosh()
- asin: ƒ asin()
- asinh: ƒ asinh()
- atan: ƒ atan()
- atan2: ƒ atan2()
- atanh: ƒ atanh()
- cbrt: ƒ cbrt()
- ceil: ƒ ceil()
- clz32: ƒ clz32()
- cos: ƒ cos()
- cosh: ƒ cosh()
- exp: ƒ exp()
- expm1: ƒ expm1()
- floor: ƒ floor()
- fround: ƒ fround()
- hypot: ƒ hypot()
- imul: ƒ imul()
- log: ƒ log()
- log1p: ƒ log1p()
- log2: ƒ log2()
- log10: ƒ log10()
- max: ƒ max()
- min: ƒ min()
- pow: ƒ pow()
- random: ƒ random()
- round: ƒ round()
- sign: ƒ sign()
- sin: ƒ sin()
- sinh: ƒ sinh()
- sqrt: ƒ sqrt()
- tan: ƒ tan()
- tanh: ƒ tanh()
- trunc: ƒ trunc()
例として整数100を表現するのに必要なビット数を求める計算をMathオブジェクトの関数を用いて行います。
まず、Math.log2関数に100を入力して、2を底とした対数をとります。この答えは入力した値が2の何乗であるのか、つまり二進数で表現した場合の桁数になります。小数を含むので、Math.ceilを使って切り上げます。答えは7になります。確かめてみると6ビットだと最大63で100には足りませんが、7ビットあると最大127で100を表現するのに不足はありません。
文字列の操作
基本操作
String組み込みオブジェクトに文字列を編集するための各種関数が用意されています。コンソールからString.prototypeと入力することで、用意されている関数の一覧を取得できます。ここでは代表的な演算子と関数をいくつか紹介します。
- 結合演算子
- substr/substring
- substrの場合、第一引数は切り出しの開始位置です。第二引数は第一引数から何文字分切り出すか、長さを指定します。
- substringの場合、第一引数は切り出しの開始位置です。第二引数は切り出しの終了位置を指定します。
- padEnd/padStart
- trim/trimEnd/trimStart
結合演算子は二つの文字列を結合して一つの文字列を作ります。
文字列の任意の位置から部分的に切り出す時にsubstr/substring関数を使います。使用目的においてはどちらの関数も同じですが、第二引数の意味が異なる点に注意が必要です。
では、実際に試してみます。
引数を(0,5)とした場合はsubstrとsubstringの結果は同じですが、第一引数が0より大きくなった場合は結果が変わってきます。substrの場合は3文字目から5文字を取るという意味になるのに対し、substringでは3文字目から5文字目までという意味になります。第二引数を省略した場合は、同じ結果になります。
padEnd/padStartは先頭・末尾に文字埋めをして指定の長さの文字列を作る関数です。実際に試してみます。
例では'example'の先頭・末尾に'.'を付加して10桁の文字列を作っています。では、'example'よりも短い桁数を指定した場合はどうなるでしょうか。実際に試してみます。
'example'よりも短い桁数を指定した場合は元の文字列のままになります。元の文字列を切り捨てる操作は行われません。
trim関数は文字列の先頭、末尾、あるいはその両方にある空白を除去した文字列を作ります。
テンプレートリテラル
正規表現
正規表現とは文字列を処理する為の共通仕様で、処理対象の文字列を表すパターンと、パターンを使った処理によって構成されます。パターンの仕様についてはここでは触れず、JavaScriptで正規表現を利用するにはどのようにコードを書けばいいのかを説明します。
- RegExpオブジェクト
RegExpオブジェクトは正規表現のパターンを表すオブジェクトです。オブジェクトを生成する為の記法として、リテラル記法とコンストラクター関数を使った方法があります。ここではリテラル記法でオブジェクトを生成する方法を説明します。
/[正規表現]/[フラグ]
指定可能なフラグについて説明します。
- i
- g
- m
- s
- u
- y
このフラグを指定すると大文字と小文字の区別を行いません。指定しない場合は大文字と小文字を区別します。
このフラグを指定すると全ての一致を検索します。指定しない場合は最初の一致のみを検索します。
このフラグを指定すると複数行の文字列に対する処理を有効にします。指定しない場合は最初の一行のみが処理の対象となります。
このフラグを指定すると'.'が改行にもマッチするようになります。指定しない場合は'.'は改行にマッチしません。
このフラグを指定するとUnicodeのサポートが有効になり、サロゲートペアの処理が可能になります。
このフラグを指定するとスティッキーモードが有効になり、lastindexプロパティで指定された位置からのみ検索を開始します。gフラグとの併用はできません。もしgとyの両方が指定された場合はgは無視されます。
const numbers = /^[0-9]+$/
const alpha = /^[a-zA-Z]+$/
numbersは「数字のみ」を表現しています。 alphaは「英文字のみ」を表現しています。
testメソッドは文字列が正規表現と一致しているかどうかを調べます。結果は真偽値で返り、trueは一致、falseは不一致です。 次にtestメソッドの構文を示します。
[RegExpオブジェクト].test([テスト対象文字列])
testメソッドはRegExpオブジェクトのインスタンスメソッドです。引数に調べる対象の文字列を指定します。 使用例を下に示します。
const numbers = /^[0-9]+$/
let testString = '1234567'
if (numbers.test(testString))
console.log(`${testString} is numbers`)
else
console.log(`${testString} is not numbers`)
testStringに設定した文字列に対して、数字のみかどうかを調べて、結果をコンソールに出力しています。 実際にコンソールで実行してみます。
testStringに'1234567'を設定して、testメソッドによるチェックを行っています。正規表現として定義したパターンは「数字のみ」で、長さは1文字以上の任意の長さです。testメソッドの結果はtrueで、if文による分岐は'is numbers'と表示する方に分岐しています。testStringに「数字のみ」の条件を満たさない文字列を設定して、testメソッドを実行してみてください。
matchメソッドを使うと、正規表現にマッチした文字列を取得できます。gフラグを指定するとマッチするものすべてを、 gフラグを指定しないと最初にマッチしたものだけを返します。結果は配列になります。
[マッチ対象文字列].match([RegExpオブジェクト])
matchメソッドはStringプロトタイプのインスタンスメソッドです。引数に正規表現を指定します。使用例を以下に示します。
const numbers = /[0-9]+/g
const testString = '123abc456def7'
const matchesArray = testString.match(numbers)
console.log(matchesArray)
実際にコンソールで実行してみます。
123,456,7という3つの要素を持った配列が出力されました。 正規表現で指定された1文字以上の数字だけの並びを'123abc456def7'から全て取り出した結果です。 abcとdefで区切られた[124]abc[456]def[7](角カッコで囲んだ部分が正規表現と一致した部分)が配列の要素になります。
概要の説明。
引数、構文
引数、構文の説明。使用例へのつなぎ。
使用例
コンソールで試す旨の説明。
実行した結果に対する説明
searchメソッドを使うと、正規表現にマッチした文字列を探し、最初にマッチした位置を返します。 マッチした文字列がない場合は-1が返ります。
const numbers = /[0-9]+/g
const testString = '123abc456def7'
const matchPos = testString.search(numbers)
console.log(matchPos)
引数、構文の説明。使用例へのつなぎ。
使用例
コンソールで試す旨の説明。
実行した結果に対する説明
制御構文
分岐(if)
if文によって特定の条件の時のみ実行する、という処理が作れます。これまで学習してきた範囲では入力した命令文や計算式は必ず実行されていましたが、if文を使うと必ずしも実行されない場合があります。構文は次の通りです。
if (条件)
実行文1
[else
実行文2]
このif文の場合、条件が成立した場合は実行文1が実行され、実行文2は実行されません。条件が成立しなかった場合は実行文1は実行されず、実行文2が実行されます。以下に例を示します。
console.logは、指定した文字をコンソール画面に表示する命令です。条件が成立する場合と成立しない場合で文言を分けて、どちらに分岐したのかをわかるようにしています。
実行文1、実行文2には更にif文を書く(これをネストや入れ子と言います)こともできます。その場合はこのように展開されます。
if (条件1)
if (条件2)
実行文1-1
[else
実行文1-2]
[else
if (条件3)
実行文2-1
[else
実行文2-2]
分岐(switch-case)
注意:変数について理解しないと理解が困難である為、教育コースの学習順としては変数の後ろに配置すること。
switch文は特定の変数がどのような値をとるか、という点に着目した分岐を作成するとき使います。 例として変数x(という名前のついたメモリ)に、商品を表す整数値が格納されているケースを想定した分岐をswitch文で表現します。
switch (x) {
case 1:
console.log('item one selected.')
break;
case 2:
console.log('item two selected.')
break;
case 3:
console.log('item three selected.')
break;
default:
console.log('othre item selected.')
}
初めにswitch (x)と記述し、xの値に注目した分岐を行う事を示します。 次にcase <値>として分岐の条件を記述します。xが<値>と等しい場合、という条件になります。 コード全体ではxが1の時、2の時、3の時の処理がそれぞれ指定されています。 breakは「この分岐の処理はここまでとする」という意味で、それぞれの分岐の最後に必ず記述してください。 defaultは「これまでのどの条件にも一致しなかった場合」という特別な意味があります。 では、xに2を設定して、コンソールで実行してみます。
case 2:の分岐に入り、'item two selected.'が表示されます。xに他の値を設定してどのような動きになるのか試してみてください。
反復(for)
注意:変数について理解しないと理解が困難である為、教育コースの学習順としては変数の後ろに配置すること。
for文によって同じ処理を同じ処理を繰り返し行えます。次のような構文になります。
for ([初期化]; [反復条件]; [式])
実行文
- 初期化
この[初期化]部分には、繰り返しを制御する変数の初期化を記述します。繰り返しが始まる前に一度だけ実行されます。 一般的な例として、変数iにゼロを代入して初期化するコードを示します。
for (i=0; [反復条件]; [式])
実行文
実行文の部分に書いた命令が、繰り返し実行されます。 ここでは例として、変数iの値をコンソールに出力するコードを示します。
for (i=0; [反復条件]; [式])
console.log(`i=${i}`)
この[式]部分は、実行文の実行が終わった後に評価されます。 一般的には、繰り返しを制御する変数に1加算を行う式が使われます。この例を示します。
for (i=0; [反復条件]; i=i+1)
console.log(`i=${i}`)
[反復条件]は[式]が評価された後にチェックされます。この[反復条件]がtrueとなった場合に、再度実行文が実行されます。 falseとなった場合には繰り返しを終了し、for文の次の文が実行されます。 例として変数iが10未満の間繰り返す、という条件のコードを示します。
for (i=0; i<10; i=i+1)
console.log(`i=${i}`)
このコードをコンソールで実行してみます。
iが0から、1加算されながら1,2,3と変化していき、10になった所で反復条件を評価した結果falseとなり、繰り返しが終わります。
反復(while)
注意:変数について理解しないと理解が困難である為、教育コースの学習順としては変数の後ろに配置すること。
while文によって同じ処理を同じ処理を繰り返し行えます。次のような構文になります。
while ([反復条件])
実行文
while文はfor文とは異なり、初期化と反復条件評価前の式がありません。 for文は一般に制御変数を使って一定の回数繰り返す為に使われるのに対し、while文はより柔軟な条件での繰り返しを行うために使います。 長さが一定の基準を超えるまで、文字列に編集を加え続ける処理を例として示します。
s = 'hello'
while (s.length<10)
s = s + '.'
console.log(`s=${s}`)
このコードをコンソールで実行してみます。
変数sの末尾に'.'を追加し、長さが10以上になったら反復を終わり、編集されたsをコンソールに出力します。 最終的に'hello.....'が出力されます。文字列の長さは10です。
例外処理
注意:変数について理解しないと理解が困難である為、教育コースの学習順としては変数の後ろに配置すること。
プログラムは実行中に処理を停止してしまう事があります。これを検知し、必要な対処を行う仕組みがあります。 これを例外処理と呼びます。例外処理の構文を示します。
try {
[例外発生可能性のある実行文]
}
catch ([例外変数]) {
[例外が発生した場合に実行する実行文]
}
finally {
[最終実行文]
}
[例外発生可能性のある実行文]は、何等かの例外が発生する可能性があり、例外処理によって例外発生時の対応が必要になる実行文です。 例えば、外部との通信処理が途中で中断するなどして例外が発生する可能性がある場合は、try-catch構文を使って例外処理を行います。
[例外変数]は、例外が発生した時に、その詳細が格納されたオブジェクトが設定される変数です。 この変数を調べることでどのような例外が発生したのかが分かります。 [例外が発生した場合に実行する実行文]は[例外発生可能性のある実行文]にて何等かの例外が発生した場合に実行されます。 では例外が発生した場合に、その詳細をコンソールに出力するコードを例として示します。
x = 1
try {
y = x.substring(1)
console.log(`y=${y}`)
}
catch (e) {
console.log('例外が発生した為、処理が中断しました')
console.log(e.toString())
}
このコードをコンソールで実行してみます。
3行目のsubstringを実行しようとしますが、xが数値でありsubstringは文字列に対する関数である為例外が発生しています。 この為、4行目のconsole.logが実行されず、7行目、8行目が実行されます。 その結果、例外が発生したことを示す文言と、例外変数を文字列化したものがコンソールに出力されます。 例外変数によると'TypeError'である事が示されており、例外発生の原因がわかります。
[最終実行文]は、例外が発生した場合もしない場合も実行される実行文です。先ほどの例にfinallyを追加してみます。
x = 1
try {
y = x.substring(1)
console.log(`y=${y}`)
}
catch (e) {
console.log('例外が発生した為、処理が中断しました')
console.log(e.toString())
}
finally {
console.log('処理を継続します')
}
このコードを実行してどの部分が実行されるかを確認してみます。 xの値は1を入れる場合と'abc'を入れた場合の2通りを試します。
xに1を設定した場合は7行目、8行目が実行されるところまでは既に説明した例と同じです。 そのあと、追加console.logが実行され'処理を継続します'がコンソールに出力されています。
xに'abc'を設定した場合はsubstringで例外は発生せずに、4行目が実行されます。 そのあと、catch部分は実行されず、finallyのconsole.logが実行され'処理を継続します'がコンソールに出力されています。
配列の操作
配列とは
注意:変数について理解しないと理解が困難である為、教育コースの学習順としては変数の後ろに配置すること。
配列とは変数の特別な形態です。通常、変数には1つの値しか設定できません。 異なる値を保持するには別の名前の変数を使う必要があります。 例えば商品の価格を格納する変数を考えてみると、複数の商品に対応するには次のように変数を宣言します。
let item1_price = 1000
let item2_price = 300
let item3_price = 800
配列を使って宣言すると、次のようになります。
let item_price = [1000, 300, 800]
個別に変数を宣言するのに比べ、簡潔に表現できます。
配列の要素の取得
では、item_priceから1つ目の商品、2つ目の商品、3つ目の商品の値を取得し、コンソールに出力するコードを示します。
console.log(item_price[0])
console.log(item_price[1])
console.log(item_price[2])
item_priceは変数名です。続く[0]は何番目の要素かを示していて、数字の部分をインデックスと言います。 インデックスは別の変数を使って指定することもできます。 次の例はfor文を使って、先ほどの例を書き換えたものです。
for (i=0; i<3; i++)
console.log(item_price[i])
実際にコンソールで実行してみます。
配列の全ての要素を出力できました。
では、配列の要素が3個ではない場合どうでしょうか。 プログラム作成時点で要素がいくつかわからない場合に対応したコードを示します。
for (i=0; i<item_price.length; i++)
console.log(item_price[i])
lengthを調べると、配列の長さがわかります。これをfor文の反復条件に使うことで、 どのような長さの配列であっても全ての要素を処理できます。
配列の要素の更新
配列の個々の要素を見るにはインデックスを使う事を示しました。 配列の要素を更新するにもインデックスを使います。 次のコードは配列の0番目の要素を1.1倍しています。
item_price[0] = item_price[0] * 1.1
インデックスを補う点以外は、通常の変数と同じように扱えます。
配列の要素の追加と削除
push関数を使うと配列の末尾に対する要素の追加ができます。 pop関数を使うと配列の末尾に対する要素の削除ができます。 次のコードはitem_priceの末尾に500と600を追加し、600を削除しています。
item_price.push(500)
item_price.push(600)
item_price.pop()
実際にコンソールで実行してみます。
push関数は要素が追加された位置(1番目から数え始めた何番目か)を返します。インデックスではない点に注意してください。
pop関数は削除された要素を返します。
push関数pop関数では末尾の要素のみ追加削除できましたが、例えば先頭の要素の削除、先頭に追加といった操作はできませんでした。 splice関数を使うと、配列の任意の位置の要素の追加と削除ができます。
splice関数で要素を追加する方法ついて説明します。splice関数を使って要素を追加するには引数を次のように指定します。
splice(追加位置,0,追加項目1,追加項目2,..,追加項目n)
第一引数として、要素を追加する位置のインデックスを指定します。先頭の場合はゼロになります。 第二引数にはゼロを指定します。削除を行う場合の削除件数を指定するのですが、要素を追加する場合はゼロを指定します。 第三引数以降に追加する要素を指定します。これは1個以上の任意の個数指定できます。
では、splice関数で配列に新しい要素を追加するコードを示します。
let item_price = [1000, 300, 800]
console.log(item_price)
item_price.splice(0,0,150)
console.log(item_price)
item_price.splice(1,0,160,170)
console.log(item_price)
実際にコンソールで実行してみます。
[1000, 300, 800]で初期化した配列に、splice(0,0,150)とすることで、先頭に150を追加しています。 次にsplice(1,0,160,170)とすることで、先頭から二番目と三番目の要素として160と170を追加しています。
splice関数で要素を削除する方法ついて説明します。splice関数を使って要素を削除するには引数を次のように指定します。
splice(削除位置,削除要素数)
第一引数として、削除する要素のインデックスを指定します。先頭の場合はゼロになります。 第二引数には削除する要素の個数を指定します。
では、splice関数で配列から要素を削除するコードを示します。
let item_price = [1000, 300, 800]
console.log(item_price)
item_price.splice(1,1)
console.log(item_price)
item_price.splice(0,1)
console.log(item_price)
実際にコンソールで実行してみます。
[1000, 300, 800]で初期化した配列に、splice(1,1)とすることで、二番目の要素である300を削除しています。 次にsplice(0,1)とすることで、先頭の要素である1000を削除しています。
lengthプロパティ
lengthプロパティを使うと配列の長さを調べられます。プロパティは引数をとりません。 lengthプロパティを使う場合は、変数名に続けて'.length'と書くだけで使用できます。 以下にlengthプロパティの使用したコードの例を示します。
let item_price = [1,2,3,4,5]
console.log(item_price.length)
item_price.push(100)
console.log(item_price.length)
item_price.splice(0,0,200,300,400)
console.log(item_price.length)
実際にコンソールで実行してみます。
[1,2,3,4,5]で初期化直後はlengthは5です。100をpushするとlengthは6になります。spliceで200,300,400を追加するとlengthは9になります。
indexOfメソッド
indexOfメソッドは配列に対する簡単な検索機能です。同じ値を持った要素のインデックスを結果値として返します。もし、同じ値を持った要素がない場合には-1を返します。indexOfメソッドを使って配列の検索をするには、引数を次のように指定します。
indexOf(検索したい値,検索開始位置)
第一引数として検索した値を指定します。第二引数として検索開始位置のインデックスを指定します。第二引数は省略可能です。省略した場合はゼロとして扱われ、先頭から検索されます。
以下にindexOfメソッドを使用したコードの例を示します。
let item_price = [1000, 300, 800]
console.log(item_price.indexOf(800))
console.log(item_price.indexOf(1000,1))
実際にコンソールで実行してみます。
[1000, 300, 800]で初期化した配列に対して、indexOf(800)とすると、800という値を持つ要素のインデックスとして2が表示されます。 次にindexOf(1000,1)とすると、1000という値を持つ要素をインデックス1から検索し、見つからない為結果として-1が表示されます。
forEachメソッド
注意:コールバック関数に関する理解が必要です。
forEachメソッドは配列の全ての要素に対して一度づつ、引数で渡されたコールバック関数を実行します。 forEachメソッドを使って処理を行うには、引数を次のように指定します。
forEach(コールバック関数(処理対象の要素,処理対象のインデックス,処理対象の配列))
コールバック関数には要素の処理に使用する関数を指定します。一般的には関数をその場で宣言する関数式やアロー関数を使います。 コールバック関数の引数のうち、処理対象のインデックスと処理対象の配列は省略可能です。
以下にforEachメソッドを使用したコードの例を示します。
let item_price = [1000, 300, 800]
let total = 0
item_price.forEach(function sum(x) {
total = total + x
})
console.log(total)
配列の要素全ての合計値を求める例です。では実際にコンソールで実行してみます。
コールバック関数として定義したsum関数が、item_priceの各要素に対して実行されます。具体的はsum(1000),sum(300),sum(800)と3回実行されます。 sum関数内部では、totalという変数に引数で受け取った要素を加算していきます。 最終的には0 + 1000 + 300 + 800が計算され、2100が最後に出力されます。
findメソッド
findメソッドは配列の要素を先頭から順に一度づつ、引数で渡されたコールバック関数を実行します。コールバック関数では要素を見つける為の条件を判定する処理を実装し、trueもしくはfalseを返します。最初にtrueと判定された要素がfindメソッドの返り値となります。該当する要素がなかった場合にはundefinedが返り値となります。
findメソッドを使うには、引数を次のように指定します。
find(コールバック関数(処理対象の要素,処理対象のインデックス,処理対象の配列))
形式的にはforEachと同様です。処理対象のインデックスと処理対象の配列も省略可能です。ただ、コールバック関数はtrueもしくはfalseを返すように実装します。
以下にfindメソッドを使用したコードの例を示します。
let item_price = [1000, 300, 800]
let target = 300
let found = item_price.find(x => x === target)
console.log(found)
変数targetに設定した値と同じ値を持った要素があるかどうかを配列の先頭から順に調べ、同じ値があった場合はその要素の値を出力します。コールバック関数の定義にはアロー関数を使用しています。
では実際にコンソールで実行してみます。
変数targetに300を設定した場合には同じ値の要素が見つかり、300が出力されます。変数targetに301を設定した場合には同じ値の要素が見つからない為、undefinedが出力されます。
filterメソッド
filterメソッドは引数で与えたコールバック関数を使って配列の要素をテストし、条件に合致した要素のみで新しい配列を作ります。コールバック関数は条件に合致した場合はtrueを、そうでない場合はfalseを返すように実装します。
filterメソッドを使うには、引数を次のように指定します。
filter(コールバック関数(処理対象の要素,処理対象のインデックス,処理対象の配列))
形式的にはforEachと同様です。処理対象のインデックスと処理対象の配列も省略可能です。ただ、コールバック関数はtrueもしくはfalseを返すように実装します。
以下にfilterメソッドを使用したコードの例を示します。
let item_price = [1000, 300, 800]
let lower_limit = 800
let upper_item_price = item_price.filter(x => x >= lower_limit)
console.log(upper_item_price)
では実際にコンソールで実行してみます。
変数lower_limitに800を設定し、filterにてitem_priceの要素のうちlower_limit以上の値持っているものだけで、新しいupper_item_priceという配列を作成しています。最後に出力されるのは800以上の値である、1000と800になります。
mapメソッド
mapメソッドは引数で与えられたコールバック関数を使って配列の要素に何等かの処理を加え、処理を加えてできた値を要素とした新しい配列を作ります。
mapメソッドを使うには、引数を次のように指定します。
map(コールバック関数(処理対象の要素,処理対象のインデックス,処理対象の配列))
形式的にはforEachと同様です。処理対象のインデックスと処理対象の配列も省略可能です。コールバック関数は新しい配列の要素となる値を返すように実装します。
以下にmapメソッドを使用したコードの例を示します。
let fruits = ['apple', 'banana', 'grape']
let many_fruits = fruits.map(x => x + 's')
console.log(many_fruits)
では実際にコンソールで実行してみます。
['apple', 'banana', 'grape']で初期化した配列fruitsに対して、mapメソッドを使って末尾に's'を付加する操作を行い、新しい配列をmany_fruitsとして受け取ります。最後は['apples', 'bananas', 'grapes']がコンソールに出力されます。
その他のメソッド
配列を操作する為のメソッドについて代表的なものを説明してきました。他にも多くのメソッドが配列の操作の為に用意されています。以下にその一覧と簡単な説明を示します。
- concat
- copyWithin
- entries
- every
- fill
- findIndex
- findLast
- findLastIndex
- flat
- flatMap
- includes
- join
- keys
- lastIndexOf
- reduce
- reduceRight
- reverse
- shift
- slice
- some
- sort
- toLocaleString
- toString
- unshift
- values
配列同士を結合して1つの配列にします。
配列の内部で値を別の場所にコピーします。
配列をインデックスをキーとして要素の値にアクセスするkey-valueペアのイテレータオブジェクトを作成します。
コールバック関数を使い、全ての要素をテストした結果が全てtrueになるかどうかを調べます。
指定した値を、配列の指定した範囲に設定します。
コールバック関数がtrueを返す最初の要素のインデックスを返します。
配列の後方から前方に対して順にコールバック関数を呼出し、trueを返す最初の要素を返します。
配列の後方から前方に対して順にコールバック関数を呼出し、trueを返す最初の要素のインデックスを返します。
配列の中に配列が含まれている時に、配列の入れ子を指定の深さに調整した新しい配列を返します。
mapメソッドを呼び出した結果に対して深さ1でflat化した新しい配列を返します。
配列に特定の値を持った要素があるかどうかを調べ真偽値で返します。要素がある場合はtrue、ない場合はfalseです。
配列の全要素を順に結合した文字列を返します。
配列の全要素のインデックス値をキーに持つイテレータオブジェクトを作成します。
配列の後方から前方に対して順に指定の値を持った要素があるかどうかを調べ、最初に見つかった要素のインデックスを返します。見つからなかった場合は-1を返します。
配列の全ての要素に対して順にコールバック関数を呼出し、最終的にコールバック関数の出力として1つの値が取れた時、その値を返します。
reduceメソッドの処理の実行順を配列の後方から前方に行うようにしたものです。
配列の要素の順番を逆順にした新しい配列を返します。元の配列も逆順になる点に注意して下さい。
配列の最初の要素を取り除き、その要素を返します。
元の配列の部分的なコピーを作成して新しい配列として返します。
コールバック関数を使い、全ての要素をテストした結果1つでもtrueになるかどうかを調べます。
配列のソートを行います。
配列の全ての要素に対してtoLocaleStringメソッドを実行し、その結果に置き換えます。
配列の要素を列挙した文字列を返します。
配列の先頭に要素を追加し、追加した後の配列の長さを返します。
配列の全要素の値を持つイテレータオブジェクトを作成します。
分割代入
分割代入とは、配列から値を取り出して個別の変数に値を代入する場合に利用できる簡易な表現です。 例えば、次のような場合に分割代入を使った表現に書き換えられます。
const a = [1, 2, 3, 4, 5]
let x,y,z
x = a[0]
y = a[1]
z = a.slice(2, 5)
これを分割代入を用いて書き換えます。
const a = [1, 2, 3, 4, 5]
let x,y,z
[x, y, ...z] = a
分割代入を使うとより簡潔に表現できます。
実際にコンソールで実行してみます。
分割代入で書き換える前と同じ結果が得られます。
オブジェクトの操作
オブジェクトの値の取得・更新
オブジェクトはその中に子供の要素を持ちます。これをメンバーと呼びます。メンバーには値を持つ変数として機能するプロパティと、関数として機能するメソッドがあります。メンバーを参照するには2つの記法があります。ドットによる記法と角括弧による記法です。
次にドットによる記法を示します。
[object名].[メンバ名]
次に角括弧による記法を示します。
[object名]['[メンバ名]']
角括弧による記法は冗長に見える事と、エディタの機能によってメンバ名がサジェストされない点が不利に見えますが、メンバ名が入っている変数を代わりに使う事ができるので、コードを記述する上での自由度が大きくなります。
const yamada = {
name: 'Yamada Taro',
address: 'Shibuya-ku,Tokyo,Japan',
age: 43
}
console.log(yamada.name)
console.log(yamada['address'])
const memberName = 'age'
console.log(yamada[memberName])
実際にコンソールで実行してみます。
yamadaオブジェクトのname,address,age全てのメンバの値を出力できました。
メンバは変数と同じように他の値を代入できます。
const yamada = {
name: 'Yamada Taro',
address: 'Shibuya-ku,Tokyo,Japan',
age: 43
}
yamada.age = yamada.age + 1
console.log(yamada.age)
このコードを実行すると44が出力されます。
オブジェクトを要素に持つ配列の操作
配列の要素としてオブジェクトを指定すると、オブジェクトを要素に持った配列を作れます。
const persons = [
{
name: 'Yamada Taro',
age: 44
},
{
name: 'Sato Jiro',
age: 51
}
]
persons.forEach(x => console.log(`${x.name},${x.age}`))
このサンプルでは、2つのオブジェクトを要素に持った配列personsを作っています。次にforEachメソッドを使い、全ての要素のnameプロパティとageプロパティをコンソールに出力しています。
実際にコンソールで実行してみます。
編集されたnameとageが全ての要素のオブジェクトに対して出力されました。
配列なので、インデックスを使って参照することもできます。
pushメソッドを使って新たなオブジェクトを追加することもできます。

Lesson 1
Chapter 2
プログラムの構造
Chapter 1では内部処理を構成する最小限の制御構造として、分岐と反復について説明しました。ただ、これだけではたくさんの機能を持った高度なプログラムを作るのは難しいです。JavaScriptはコードの再利用可能な部分を、様々な形で再利用できるようにまとめることができます。Chapter 2ではコードを再利用できるようにまとめる方法について説明します。
関数
関数とは、一連のコードの集まりに名前をつけ、名前によってコードの集まりを実行できるようにしたものです。引数を入力として、戻り値を出力します。 次のように宣言します。
function [関数名]([引数1], [引数2], ...[引数n]) {
[実行文]
[実行文]
...
[実行文]
}
次のコードは関数を宣言して使う例です。
function addOne(value) {
return value + 1
}
console.log(addOne(100))
addOneという名前の関数を宣言しています。この関数はvalueという引数を一つ受け取ります。valueに1を加えた値をreturn文で返り値としています。
この例では整数を返していますが、文字列、真偽値の他、配列、オブジェクトや関数を返り値として返すことができます。
この例ではnameとageを引数で受け取り、それぞれをメンバとしたオブジェクトを返り値として返しています。
再帰関数
再帰関数とは、処理の過程で自分自身を呼び出す関数のことです。
フィボナッチ数列のn番目の数を求める関数を例として示します。
function fibonacci(n) {
if (n === 1 || n === 0) {
return 1
}
return fibonacci(n-2) + fibonacci(n-1)
}
実際にコンソールで実行してみます。
実際にフィボナッチ数列が出力されていることがわかります。
高階関数
高階関数とは、関数同士を組み合わせて使う技術についてつけられた名前で、関数を引数として受け取る関数、あるいは関数を返り値として出力する関数を指します。 関数を引数として受け取る関数としては配列を処理するmapやforEachなどが高階関数になります。これらの関数は既に説明済みですので、ここでは関数を返り値として出力する関数の例を示します。
function getAdder(addValue) {
return function(value) {
return value + addValue
}
}
実際にコンソールで実行してみます。
getAdder関数を定義した後、100を引数にgetAdder関数を呼出し、返り値をadderに代入します。adderは引数で渡した100を受け取った引数に加算して返す関数です。adder(5)とすると5+100を計算して返しています。adder(100)とすると100+100を計算して返しています。
関数式(匿名関数、無名関数、関数リテラル)
これまで関数はfunction文を使って名前を付けて宣言するものとして説明してきました。JavaScriptには関数式があり、関数を変数に代入できます。 実質的には変数名を新たな関数名として扱えるようになります。関数式の使用例を下に示します。
function addTen(value) { return value + 10 }
const altAddTen = function (value) { return value + 10 }
addTen関数はこれまで通りfunction文を使って関数を宣言したものです。addTen関数とaltAddTenは実質的に同じです。alAddTenの右辺のfunctionには名前の定義がありません。このことから無名関数、匿名関数などと呼ばれることもあります。また、関数リテラルと呼ばれることもあります。
実際にコンソールで実行してみます。
addTenとaltAddTenに同じ引数を与えると、同じように10を加えた値を返します。function文を使って宣言した関数と、関数式を使って変数に格納した関数も、どちらも同じように使えます。
アロー関数式
アロー関数式とは、関数式を簡略化した記法です。簡略化されている為、メソッドやコンストラクターとしての使用ができなかったり、yieldを使用することができない等の制限があります。
function addTen(value) { return value + 10 }
const altAddTen = function (value) { return value + 10 }
const arrowAddTen = value => value + 10
3行目がアロー関数式を使った関数定義になります。'=>'の左側に入力引数を、右側に処理を記述します。2行目の関数式に比べfunctionキーワード、引数の前後の括弧、return文、return文前後の括弧が省略されています。
実際にコンソールで実行してみます。
addTenとaltAddTenと同様にarrowAddTenも同じ引数を与えると、同じように10を加えた値を返します。
クロージャ
注意:レキシカルスコープについて理解していないと理解が困難。
名前付き引数
名前付き引数とは、関数を呼び出す時に個々の引数に名前をつけて可読性を高めるための仕組みです。JavaScriptではオブジェクトを使って表現します。 例えば次のような関数を見てみます。
function getRepeatedString(stringToRepeat,timesToRepeat) {
let repeated = ''
for (i=0; i<timesToRepeat; i++) {
repeated = repeated + stringToRepeat
}
return repeated
}
getRepeatedString('REPEAT!',100)
この関数は、関数定義の部分では変数名から使用目的がわかるように書かれています。しかし、関数を呼び出す所を見てもどのように使われるのかわかりません。では、この関数の引数をオブジェクトを使って表現しなおしてみます。
function getRepeatedString({stringToRepeat,timesToRepeat}) {
let repeated = ''
for (i=0; i<timesToRepeat; i++) {
repeated = repeated + stringToRepeat
}
return repeated
}
getRepeatedString({stringToRepeat:'REPEAT!',timesToRepeat:100})
関数を呼び出す部分のパラメータの記述で、値に対応する変数名が明示されるようになりました。これにより、関数を実行した時のふるまいを予測しやすくなっています。
クラス
クラスはオブジェクトのひな形を定義したもので、意味のある変数や関数の集まりに名前をつけたものです。クラスを使うと同じメンバを持ったオブジェクトを作ることが簡単になります。
フィールドとメソッド
クラスの中には関数以外の値を保持するフィールドと、関数を保持するメソッドを定義できます。
class Person {
name
age
toString() {
return `name:${this.name},age:${this.age}`
}
}
nameとageがフィールドです。toStringがメソッドになります。toStringメソッド内でnameフィールド、ageフィールドの前にthis.という記述があります。これは、オブジェクト化された時のオブジェクト名の変わりになるものです。このように宣言されたPersonクラスを実際に使うコードを以下に示します。
let yamada = new Person()
yamada.name = 'Yamada Taro'
yamada.age = 31
console.log(yamada.toString())
最初にnew Person()としてクラスをひな形としてオブジェクトを生成し、変数yamadaに代入しています。 new クラス名とするとクラスからオブジェクトが作られます。 Personはnameフィールドとageフィールドを持っていて、yamada.name、yamada.ageとすることで、 それぞれを参照、値の設定ができます。
コンストラクタ
コンストラクタはクラスからオブジェクトを生成する時に呼び出される特別な関数です。主にフィールドの初期化を行うのに使います。 具体的には、このように使います。
class Person {
name
age
constructor() {
this.name = 'Jhon Doe'
this.age = 0
}
toString() {
return `name:${this.name},age:${this.age}`
}
}
nameフィールドに'Jhon Doe'、ageフィールドに0を初期値として設定しています。これを利用するコードを示します。
let yamada = new Person()
console.log(yamada.toString())
実際にコンソールで実行してみます。
コンストラクタで設定した初期値が使われています。次の例はコンストラクタに対して引数を設定するものです。
class Person {
name
age
constructor(name,age) {
this.name = name
this.age = age
}
toString() {
return `name:${this.name},age:${this.age}`
}
}
このクラスを利用するコードはこのようになります。
let yamada = new Person('Yamada Taro',20)
console.log(yamada.toString())
実際にコンソールで実行してみます。
このように、フィールドの初期値をコンストラクタの引数で与えると、クラスからオブジェクトを生成する処理が簡潔に記述できます。
プライベートフィールドとプライベートメソッド
通常クラス内で宣言したフィールドとメソッドは全て、クラスの外部から利用できるようになっています。しかし、クラスの内部でのみ利用可能としたい変数や関数も宣言できます。クラスの内部でのみ利用可能なフィールドをプライベートフィールド、クラスの内部でのみ利用可能なメソッドをプライベートメソッドといいます。下に例を示します。
class Person {
#name
#age
constructor(name,age) {
this.#name = name
this.#age = age
}
#getSummary() {
return `name:${this.#name},age:${this.#age}`
}
summary() {
return this.#getSummary()
}
}
プライベートにするには先頭に#を付けます。この例ではコンストラクタとsummaryメソッド以外は全てプライベートになっていて、クラス外部からは利用できなくなっています。では、これらのプライベートになったフィールドとメソッドを外部から利用しようとすると何が起こるのかを試してみます。
let yamada = new Person('Yamada Taro',20)
yamada.#name
yamada.#getSummary()
yamada.summary()
実際にコンソールで実行してみます。
プライベートフィールドとプライベートメソッドを利用しようとするとSyntaxErrorが発生して、利用できなくなっています。
ゲッターとセッター
クラスにはメソッド(関数)とフィールド(変数)があることを説明しました。ゲッターとセッターは、その中間のような存在で、利用する側からはフィールドのように扱え、クラスの定義としてはメソッドのようになります。 クラス定義の例を下に示します。
class Person {
name
#age
constructor() {
this.name = 'Jhon Doe'
this.#age = 0
}
get age() {
return this.#age
}
set age(value) {
if (/\D/.test(value)) {
throw new TypeError('ageは整数のみを受け取ります。')
}
this.#age = Number(value)
}
}
get age()がゲッターの宣言です。ゲッターは関数のようにreturn文を使って返す値を編集できます。ここではプライベート化した#ageフィールドの代わりに値を返すゲッターを定義しています。 set age(value)がセッターの宣言です。セッターは、利用する側でageに対する代入文が実行するときに呼び出される関数です。この例ではvalueに数字以外の文字が含まれる場合にTypeErrorの例外を発生させ、数字のみの場合には#ageフィールドにvalueを数値化した値を代入しています。このように、ゲッターとセッターを使うとフィールドを利用する前に何等かの変換や、入力のチェックなどができます。 このクラスの利用例を下に示します。
let yamada = new Person()
yamada.name = 'Yamada Taro'
yamada.age = 'a'
yamada.age = 20
このように、ageは通常のフィールドのように利用できます。 実際にコンソールで実行してみます。
yamada.ageに'a'を代入しようとすると、TypeErrorが発生しています。これはセッターのコードが動いた結果です。整数である20の代入は成功します。
プロトタイプ
オブジェクト同士の継承関係を表現する手法として、JavaScriptではプロトタイプというフィールドを使用します。
staticフィールドとstaticメソッド
これまでフィールドとメソッドはオブジェクトを生成して利用してきました。staticフィールドとstaticメソッドはオブジェクトではなく、クラス名を修飾して利用します。例として、Personクラスから生成したオブジェクトの数を数えるstaticフィールドをPersonクラスに追加します。
class Person {
static #numberOfPersons = 0
name
age
constructor(name,age) {
this.name = name
this.age = age
Person.#numberOfPersons = Person.#numberOfPersons + 1
}
static get numberOfPersons() {
return Person.#numberOfPersons
}
}
このnumberOfPersons staticフィールドは生成されたオブジェクトのフィールドではありません。Personクラスに付随するフィールドです。使うときはクラス名.フィールド名という形になります。
let yamada = new Person('Yamada Taro',20)
let sato = new Person('Sato Jiro',30)
console.log(Person.numberOfPersons)
実際にコンソールで実行してみます。
Personクラスからyamadaとsatoというオブジェクトを生成しています。Personクラスのコンストラクタが2回実行され、Personクラスのフィールドである#numberOfPersonsが2回、1加算されます。最後にPerson.numberOfPersonsを出力すると2が出力されます。
サブクラス
JavaScriptでは関数や変数の集まりとしてクラスを定義できる事を説明しました。クラスは単体で宣言するだけではなく、他のクラスと親子の関係を持ちます。他のクラスの子にあたるクラスのことをサブクラスと言います。
クラスが別のクラスのサブクラスである事を宣言するにはextendsキーワードを使います。
class Person {
name
age
constructor(name,age) {
this.name = name
this.age = age
}
}
class Employee extends Person {
hireDate
}
この例ではPersonクラスのサブクラスとしてEmployeeクラスを定義しています。Employeeクラスでは独自のフィールドとしてhireDateを持っています。このEmployeeクラスを利用する例を示します。
let yamada = new Employee('Yamada Taro',20)
Employeeクラスにはコンストラクタが定義されていません。 実際にコンソールで実行してみます。
nameとageを引数とするコンストラクタはPersonクラスに定義されていて、これが実行されています。nameフィールドとageフィールドもEmployeeクラスには定義されていませんが、Personクラスには定義されています。このように、extendsキーワードで指定した親クラスのフィールド、メソッドは子クラスから利用できるようになります。これを継承といいます。継承は直接の親だけでなく、親の親、さらにその親と、さかのぼれる限りさかのぼって適用されます。最終的にはObjectクラスが全てのクラスの親になります。
メソッドのオーバーライド
先ほどの例ではEmployeeクラスのhireDateに初期値を設定する処理がありませんでした。Employeeクラスにコンストラクタを実装して、hireDateに本日日付を設定するようにします。nameとageは従来通り受け取るようにします。
class Person {
name
age
constructor(name,age) {
this.name = name
this.age = age
}
}
class Employee extends Person {
hireDate
constructor(name,age) {
super(name,age)
this.hireDate = new Date
}
}
Employeeクラスのコンストラクタ内にsuper(name,age)というコードがあります。これは継承元のクラスのコンストラクタを呼び出すコードです。継承元のクラスの事をスーパークラスと呼びます。では、このEmployeeクラスを使った例を示します。
let yamada = new Employee('Yamada Taro',21)
実際にコンソールで実行してみます。
先ほどの例ではEmployeeクラスのhireDateが未設定だった為、undefinedと表示されていました。今回、Employeeクラスのコンストラクタでコンストラクタが実行された時点で初期値を設定しているので、日付時刻が表示されています。 このように、サブクラスでスーパークラスと同じ名前のメソッドを定義した場合はサブクラス側のメソッドが実行されます。これをメソッドのオーバーライド、またはシャドウイングと言います。
クラス式(無名クラス、匿名クラス)
これまでの説明では、クラスを定義する為にクラス宣言を使ってきました。ここではクラス式を使った定義方法について説明します。クラス宣言とクラス式は関数における関数宣言と関数式に対応するものです。
let Person = class {
name
age
constructor(name,age) {
this.name = name
this.age = age
}
}
クラス式による定義では、関数式のように左側にクラスを使うときの名前を置き、右側にclassキーワードとクラスの本体を置きます。イコールで結ぶことで、左側の名前で定義されたクラスが使えます。
let yamada = new Person('Yamada Taro',20)
クラス式で定義しても、クラス宣言で定義した場合と同じようにクラスを使えます。実際にコンソールで実行してみます。
クラス式でクラスを定義する場合、classキーワードに続いたクラス名を省略できます。クラス名を省略して定義したクラスを無名クラス、匿名クラスと呼びます。
this
クラス内部で定義したメソッドからクラスで定義したフィールドやメソッドを利用する場合は自分自身のオブジェクトを表すthisキーワードを使用します。
class Person {
name
age
constructor(name,age) {
this.name = name
this.age = age
}
}
この例ではコンストラクタでnameフィールドとageフィールドに引数で受け取った値を設定しています。
モジュール
モジュールとは
JavaScriptでのモジュール
再利用可能な機能をファイル単位でまとめて共有する仕組みです。ファイルの拡張子はjsです。モジュールを使って再利用できるものは名前のついたクラス、関数、変数になります。モジュールとして利用できるようにするにはexport文、モジュールを利用するにはimport文を使います。
モジュールのエクスポートとインポート
実際にクラスをモジュールとして利用できるようにして、別のプログラムからモジュールを利用する例を示します。モジュールとして利用できるようにするにはexport文を使います。モジュールを利用する側ではimport文を使ってモジュールの利用を宣言します。下はモジュールのエクスポートの例です。export-example.jsという名前のファイルとして保存して下さい。
export class Person {
name
age
constructor(name, age) {
this.name = name
this.age = age
}
get summary() {
return `name:${this.name},age:${this.age}`
}
}
下はモジュールのインポートの例です。import-example.jsという名前のファイルとして保存してください。
import { Person } from './export-example.js'
class Employee extends Person {
hireDate
constructor(name, age) {
super(name, age)
this.hireDate = new Date
}
get summary() {
return `${super.summary},hireDate:${this.hireDate}`
}
}
let yamada = new Employee('Yamada Taro', 22)
console.log(yamada.summary)
このサンプルプログラムをnode.jsを利用して実行します。node.jsのインストール方法についてはこちら(別途記述)で説明します。node.jsでプログラムを動作させるにあたり、次の設定ファイルをpackage.jsonというファイル名で作成してください。export-example.js、import-example.js、package.jsonは全て同じディレクトリに配置します。
{
"type": "module"
}
実際にnode.jsで実行してみます。
node import-example.jsでimport-example.jsを実行しています。import-example.jsではそのファイル内に定義されていないPersonクラスを継承して利用しています。Personクラスの定義が見つからなければエラーで実行できませんが、import文を使い、export-example.jsのPersonクラスをインポートしているのでエラーとはなりません。 このように、モジュールを使うとプログラムを複数のファイルに分割して管理できるようになります。
モジュールの名前付きインポート
モジュールに分割されたプログラムから、必要な要素をインポートして利用できる事を説明しました。モジュールに分割された各種機能を再利用する場合、同じ名前が使われる場合があります。同じ名前が使われているとエラーが発生してそのままでは利用できません。 このような場合に、importするものに別名を付けてエラーを回避できます。下はPersonクラスにHumanという別の名前を付けてインポートする例です。
import { Person as Human } from './export-example.js'
class Employee extends Human {
hireDate
constructor(name, age) {
super(name, age)
this.hireDate = new Date
}
get summary() {
return `${super.summary},hireDate:${this.hireDate}`
}
}
let yamada = new Employee('Yamada Taro', 22)
console.log(yamada.summary)
Employeeクラスを定義する箇所でもextends Humanとしていて、Personという名前では利用していません。では、実際にnode.jsで動かしてみます。
結果は先ほどと同様です。importする時に別名をつけると、別名で扱えるようになります。逆に、元の名前は使えなくなります。
名前付きインポートのもう一つの方法について説明します。インポートするモジュールに含まれる全ての要素を含む、オブジェクトを定義して、このオブジェクト名の修飾をしてインポートする機能を利用するというものです。下に例を示します。
import * as Human from './export-example.js'
class Employee extends Human.Person {
hireDate
constructor(name, age) {
super(name, age)
this.hireDate = new Date
}
get summary() {
return `${super.summary},hireDate:${this.hireDate}`
}
}
let yamada = new Employee('Yamada Taro', 22)
console.log(yamada.summary)
機能としては先ほどの例と変わりませんが、Personクラスを利用するにはHumanというオブジェクト名で修飾する必要があります。PersonクラスがHumanオブジェクトのメンバとなったように記述します。モジュール全体が新しい名前空間に入ったように扱えるので、個々の名前の衝突を個別に確認する必要がなくなります。
モジュールの動的インポート
注意:非同期処理とPromiseを理解していないと動的インポートは理解が困難。
機能を外部ファイルからインポートして利用するモジュールですが、いつ、どのようにインポートという処理が行われるかを説明します。これまでに説明してきたimport文はプログラムの他の命令文に先んじて同期的に実行されます。つまり、import文が実行されている間、他の機能はブロックされます。サーバサイドのプログラムで、ローカルファイルからのimportであれば処理時間は短くて済みますが、クライアントサイドのプログラムで、Webサーバからのimportである場合には通信帯域とファイルの大きさに比例して処理時間が長くなります。クライアントサイドでimport中にUI処理がブロックされると、スムースなUIを実現できません。
この問題を解決する為にモジュールの「動的インポート」機能があります。動的インポートを利用するとインポート処理は非同期に実行され、処理中に他の処理がブロックされません。 具体的にはimport関数を利用します。この関数は非同期に処理を行うもので、promiseを結果値として返します。引数はインポート対象のファイル名です。
import ('./export-example.js')
.then((ImportedModule) => {
let Employee = class extends ImportedModule.Person {
hireDate
constructor(name, age) {
super(name, age)
this.hireDate = new Date
}
get summary() {
return `${super.summary},hireDate:${this.hireDate}`
}
}
let yamada = new Employee('Yamada Taro', 22)
console.log(yamada.summary)
})
console.log('preparering module...')
import関数の返すpromiseに対してthenが受け取るresolveコールバック関数の引数にインポートされたモジュールオブジェクトが設定されています。例ではImportedModuleという名前になっていて、インポートされたPersonクラスはImportedModule.Personと記述することで利用できます。
実際にnode.jsで実行してみます。
import関数は実際にインポート対象のファイルのインポートが終わる前に呼び出し元に制御を戻し、次のconsole.logを実行して'preparering module...'を出力しています。インポートが終わった所でthenのコールバック関数が実行され、Personクラスからオブジェクトが生成され、summaryが出力されます。
パッケージ化
一旦保留

Lesson 1
Chapter 3
データの型・構造
変数と定数
var
varは互換性の為に残されている古い変数宣言のキーワードです。意図しない副作用があり、新たなコードでの使用は非推奨です。ここでは既に書かれているコードの理解の為に仕様を説明します。
varは再代入と再宣言が可能な変数である事を宣言するキーワードです。varキーワードによる宣言は、記述している位置にかかわらずコードの実行よりも前に変数を生成します。但し、初期化は行いません。例として次のコードを示します。
console.log(x)
var x = 5
console.log(x)
varによる変数xの宣言は、最初のconsole.logよりも先に行われるので、xへの参照は成功します。但し、値は初期化されないのでundefinedです。 次にx = 5の初期化が行われるので、最後のconsole.logでは5が出力されます。このようなvarによる宣言が先に行われる仕組みを巻き上げと呼びます。
let
letは再代入が可能な変数である事を宣言するキーワードです。処理の過程で内容が変化していくプリミティブ型の値を保持する変数はletキーワードで宣言します。初期化は必須ではありません。
let a
console.log(`a is ${a}`)
let b = 'B'
console.log(`b is ${b}`)
const
constは初期化が必須で再代入ができない変数である事を宣言するキーワードです。再代入が制限されているだけで、例えば、オブジェクトのフィールドなどは更新できる為、constを使って宣言しているから定数であるとは言えません。JavaScriptにおいて定数宣言専用の構文がない為、constキーワードとプリミティブ型のリテラル値で初期化された変数を便宜的に定数と呼んでいます。
const c = 'C'
console.log(`c is ${c}`)
const d = { key:'d',value:'this is D' }
console.log(`d.value is ${d.value}`)
d.value = 'this is E'
console.log(`d.value is ${d.value}`)
d = { key:'e',value:'this is E' }
この例ではcは定数ですが、dはオブジェクト内のフィールドの値が変更できるので定数とは言えません。最後のdに対する代入文は実行時エラーとなります。
リテラル
JavaScriptにおけるリテラルとは、値を表現する為の表記法です。以下の7種類のリテラルがあります。
- 真偽値リテラル
- 数値リテラル
- 浮動小数点リテラル
- 文字列リテラル
- オブジェクトリテラル
- 正規表現リテラル
- 配列リテラル
真偽値リテラル
真偽値は真値と偽値の2種類があります。真値はtrue、偽値はfalseで表します。
数値リテラル
整数を表すリテラルで、10進数の他、2進数、8進数、16進数で表現できます。表記法は次の通りです。
- 10進数
- 2進数
- 8進数
- 16進数
先頭1文字目がゼロでない数字の並び。
先頭が0bから始まる0と1の数字の並び。
先頭が0、または0o、または0Oから始まる0~7の数字の並び。
先頭が0x、または0Xから始まる0~9の数字とA~Fのアルファベットの並び。
以下が数値リテラルの例です。
1560
0b11000011000
0o3030
0x618
この例は全て10進数では1560になります。
浮動小数点リテラル
小数点以下の数字、または安全に表現できる範囲を超えた整数を表現する場合のリテラルです。10進数で表現され、正負の符号と、整数部、小数点、小数部と指数部で表現します。
以下が浮動小数点リテラルの例です。
3.14159
-1.1E+5
.5E-13
-1.1E+5の読み方は、-1.1 * (10 ** 5)です。-1.1まではそのまま読み取ります。E+5は指数部と言い、先行する-1.1に対して乗ずる数を表します。E+5で10の+5乗という意味になります。
.5E-13を先ほどの規則に則って読み取ると、0.5 * (10 ** (-13))になります。.5は0.5のゼロを省略した表現です。指数部のE-13は10の-13乗(つまり1/(10の13乗))という意味です。
文字列リテラル
文字列リテラルはシングルクォートまたはダブルクォートで囲われた文字列です。但し、シングルクォートで始まった場合はシングルクォートで、ダブルクォートで始まった場合はダブルクォートでそれぞれ閉じなければなりません。バッククォートを使った場合はテンプレートリテラルになります。
文字列リテラルの例を以下に示します。
'hello JavaScript'
"hello ECMAScript"
`hello ${scriptName}`
最後の例はテンプレートリテラルです。${scriptName}の部分はscriptName変数に格納されている値が文字列として展開されます。
オブジェクトリテラル
オブジェクトリテラルは波括弧(なみかっこ、ブレース)で囲われたプロパティ名と値の組み合わせの並びです。プロパティ名と値の組を一つも含まない場合もオブジェクトリテラルです。
オブジェクトリテラルの例を以下に示します。
let emptyObject = {}
let simpleObject = {
name: 'Yamada Taro',
age: 20
}
let includeFunctionObject = {
name: 'Sato Jiro',
age: 20,
isAdult: function() {
return (this.age >= 18)
}
}
emptyObjectはプロパティ名と値の組を一つも含まないオブジェクトリテラルの例です。simpleObjectはnameとageというプロパティと値を持つオブジェクトリテラルの例です。includeFunctionObjectは関数をプロパティに持つオブジェクトリテラルの例です。クラス定義と同様にthisキーワードを使い、自分自身のプロパティを参照できます。
正規表現リテラル
正規表現リテラルは、正規表現のパターンをスラッシュで囲ったものです。
正規表現リテラルの例を下に示します。
let regexp = /H...o/
この例では、Hで始まり、任意の3文字の後oで終わるパターンを正規表現リテラルとして定義しています。
配列リテラル
配列リテラルとは、角括弧(かくかっこ、ブラケット)で囲われた式または値の並びです。式や値を一つも含まない場合も配列リテラルです。
配列リテラルの例を以下に示します。
let greetings = ['こんにちは', 'Hello', 'Buon' + 'giorno', getChineseHello()]
式は評価された後に配列の要素となります。例では'Buon' + 'giorno'という文字列の結合とgetChineseHello()という関数が呼び出された後、配列としてgreetingsに代入されます。
データの型
JavaScriptは「動的型付けの言語」と言われます。これは宣言時に変数にデータ型が与えられているわけではなく、実行時に値が代入されることによって値が持っているデータ型が与えられるという事を表しています。変数はどのような値も代入できるので特定のデータ型を持たず、値がデータ型を持っています。
JavaScriptは組み込みで9つのデータ型を持っています。以下に9つのデータ型を示します。
- プリミティブ型
- 論理型
- 数値型
- BigInt
- 文字列型
- undefined
- Symbol
- オブジェクト型
- Function
- null
プリミティブ型
JavaScriptの組み込みデータ型のうち、6つはプリミティブ型に分類されます。プリミティブ型とはデータ型の原子にあたるものです。プリミティブ型はこれ以上分解できないデータであり、値は変更できないという性質を持ちます。
- 論理型
- 数値型
論理型のデータは真偽値を表現する型でtrueかfalseの値を持ちます。
JavaScriptには2種類の数値を表現する型があります。NumberとBigIntです。ここではNumberについて説明します。Numberは内部的には64bitで表現された浮動小数点数です。符号ビットが1bit、指数部が11bit、仮数部が52bitで構成されます。整数で誤差なく正確に表現できる範囲は-(2 ** 53 - 1)~(2 ** 53 - 1)までの数値になります。整数がこの範囲内であるかどうかを調べるNumber.isSafeIntegerという関数と、Number.MAX_SAFE_INTEGER、Number.MIN_SAFE_INTEGERという定数があります。
特殊な値として+Infinity、-Infinity、NaNの3つがあります。Infinityは文字通り無限を表す値でゼロ除算をすると出力されます。NaNは'Not a Number'の略で「数ではない」事を表す特殊な値です。
NaNは等価演算子で比較すると特殊なふるまいをする値です。次のコードはfalseを返します。
NaN == NaN
NaN === NaN
NaNであるかどうかを調べるにはisNaN関数を使います。次のコードはtrueを返します。
isNaN(NaN)
BigIntはNumberで表現可能な範囲を超えて数値を表現できる型です。リテラルの末尾にnを付加するか、BigIntのコンストラクタを使う事でBigInt型の値を取得できます。
下にBigIntを使った例を示します。
Number.MAX_VALUEに1を加算する例です。Number型では指数を使った表現しかできず、値の変化も見られません。BigIntとして計算すると整数として最終桁まで表示され、1加算しても正確に値が出力されています。
文字列型は文字通り文字列、テキストを表現する型です。文字はUTF-16で表現されてメモリに格納されています。
undefinedは型であり、値でもあります。何等かの変数を宣言して未初期化の場合、undefinedが初期値になります。関数は値を明示的にreturnしない場合、undefinedが返り値になります。
SymbolはSymbol関数によって生成される実行環境上で一意な値です。一意である、という特性を利用してオブジェクトのプロパティとして利用する場合があります。
オブジェクト型
オブジェクト型は複数のデータ型の組み合わせで作るデータ構造です。任意のユーザ定義のオブジェクト型が定義できますが、組み込みでnewキーワードで作ることができる多くのオブジェクト型があります。
- Array
- Map
- Set
等々…
Function
Functionは関数を表すデータ型です。JavaScriptでは関数を値として扱えるので、変数に代入したり引数や戻り値にできます。
null
nullは型であり、値でもあります。nullという型・値はundefinedと類似した用途で不能値として扱われることがありますが、明確な意図をもって使い分けるべきです。undefinedは「評価時点で初期化されていない」という意味があります。nullは「他のどのようなオブジェクトでもない」という意味になります。
nullとundefinedについては等価演算子とisNaN関数のふるまいにも注意するべきです。以下に例を示します。
undefined == null
undefined === null
isNaN(undefined)
isNaN(null)console-null-example.png
実際にコンソールで実行してみます。
等価演算子で比較するとtrueになりますが、厳密等価演算子ではfalse。undefinedはisNaNがtrueになりますが、nullの場合はisNaNがfalseになります。
なお、typeof演算子でnullを評価すると型名として'object'が返るのは互換性の為に残された歴史的経緯のあるバグであり仕様です。nullの型はnull型です。
型の変換
何等かの機能を実装している時に、数値型は数値型だけ、文字列型は文字列型だけで処理をするケースはあまりありません。利用者に処理の結果を出力するには文字列として出力する必要がありますし、利用者からの入力を内部で計算が可能な型に変換する事もよくある事です。
ここでは、よく使われる数値と文字列の間の型変換、日付と文字列の間の型変換について説明します。
Numberクラス
数値と文字列の変換にはNumberクラスのメソッドを使用します。
- Number
- toString
- toFixed
文字列を入力として、Number型の値を生成します。parseIntやparseFloatは浮動小数点表現に対応していなかったり、英文字や記号が混入した場合に意図しない値を返すなど、安全とは言えないふるまいをします。Number関数の利用を推奨します。
NumberクラスのtoStringメソッドを使うと、数値を文字列に変換できます。引数として何進数の数として変換するかを指定でき、2進数、8進数、10進数、16進数が指定できます。引数を省略した場合は10進数になります。
NumberクラスのtoFixedメソッドを使うと、数値の小数点以下の数値の桁数を指定して文字列化できます。表現するのに必要な桁よりも少ない桁数が指定された場合は、自動的に丸められるので注意が必要です。必要な切り捨てや切り上げなどの操作はMathクラスのメソッドを利用してください。
Dateクラス
日付と文字列の変換にはDateクラスのメソッドを使用します。
- toUTCString
- toISOString
- toLocaleString
toUTCStringメソッドは日付時刻を文字列に変換します。日付時刻はUTC(協定世界時=Coordinated Universal Time)として表現されます。
toISOStringメソッドは日付時刻を文字列に変換します。日付時刻はISO形式で表現されます。タイムゾーンはゼロオフセットになります。文字列の末尾にはゼロオフセット(Zulu time)である事を表す'Z'が付きます。本質的にはtoUTCStringの出力と同じ意味ですが、出力様式が異なります。
toLocaleStringメソッドは日付時刻を文字列に変換します。様式は言語に応じてフォーマットされますが、実装に依存します。つまり、ブラウザ毎に結果が異なる可能性があります。将来のバージョンアップでも同じ様式となるかどうかは保障されません。
以下にそれぞれのメソッドの出力例を示します。
データ構造の表現
JSON
JSONはJavaScriptのオブジェクトリテラルの表記法の一部を一般化したデータフォーマットです。 JavaScriptのオブジェクト表現そのものである為、文字列として表現可能なオブジェクト型のデータであればそのまま表現できます。 JSONはJavaScript Object Notationの省略形です。
オブジェクトからJSONへの変換は、JSON.stringfifyメソッドを利用します。 JSON.stringifyの出力はJSONフォーマットに則った文字列に過ぎません。 なので、文字列を使った通信が可能であれば、相互にJSONフォーマットでのデータ交換が可能です。 FrontとAPI間のデータ授のフォーマットにはJSONが使われることが一般的です。
JSONからオブジェクトへの変換は、JSON.parseメソッドを利用します。 JSONフォーマットのデータを受け取った場合はJSON.parseメソッドの引数にJSONデータを渡します。 JSON.parseメソッドはJSONデータからオブジェクトデータを生成して返します。
XML
XMLはマークアップ言語の一種で、HTMLと同じようにSGMLという言語から派生したものです。 値またはタグをタグで囲むというシンプルな仕組みで構造化データを記述します。 初版が定義されたのは1998年と古く、多くの標準に採用されています。 XMLはExtensible Markup Languageの省略形です。
HTMLはタグが既に規定されているのに対し、XMLは利用者が任意のタグを定義して利用できます。 この為、どのようなデータ型であっても新たにタグを定義することで表現できます。 この自由度の高さがExtensible=拡張可能という語に現れています。 構造化されたデータはタグを入れ子にすることで表現します。
DOMはXML/HTML文書を木構造のデータとして扱えるようにするAPIです。 JavaScriptはDOMを通じてXML/HTML文書を読み取ったり書き換えたりできます。 FrontはDOMを使って利用者と対話(文書の値の読み取り、文書の書き換え)します。
スコープ
プログラム言語で変数や関数などのオブジェクトを宣言した場合に、宣言が有効な範囲が決められています。これをスコープと言います。スコープを理解してしないと、必要な場所で参照できないオブジェクトを作ってしまいます。スコープを理解し適切な宣言をしてください。
スコープにはグローバルスコープとローカルスコープがあります。
- グローバルスコープ
- ローカルスコープ
グローバルスコープで宣言されたオブジェクトは、プログラム内のどの場所からでも参照できます。プログラムの様々な場所で使うオブジェクトはグローバルスコープで宣言します。 但し、グローバルスコープのオブジェクトを多用するとプログラムの理解や保守が難しくなるという副作用があります。全てをグローバルスコープにすれば良いというわけではありません。
ローカルスコープで宣言されたオブジェクトは、同一もしくは子供のスコープの範囲内でのみ参照できます。 スコープが異なると、別のスコープで宣言されているものと同じ名前のオブジェクトを宣言できます。letやconstキーワードでは二重宣言を禁止していますが、スコープが異なれば可能になります。同じ名前のオブジェクトが異なるスコープで宣言された場合で、どちらもアクセス可能なスコープの場合は同じスコープで宣言されたものが有効になり、異なるスコープのものは使用されません。これをシャドウイング(shadowing)と呼びます。ローカルの宣言で覆い隠され影になってしまうという意味あいです。
では以下のサンプルでスコープとシャドウイングの実例について説明します。なお、この例はスコープとシャドウイングを短いコードで説明するための極端な例です。実際のプログラムでは無意味に同一の名前を使用しないように、意味のある名前をつけてください。
let x = 'this variable declared at global.'
function localLevel1() {
function localLevel2() {
let x = 'this variable declared at localLevel2.global shadowed.'
console.log(`3:${x}`) // third
}
console.log(`2:${x}`) // second
localLevel2()
}
console.log(`1:${x}`) // first
localLevel1()
{
let x = 'this variable declared at block.global shadowed.'
console.log(`4:${x}`) // fourth
{
console.log(`5:${x}`) // fifth
}
{
let x = 'this variable declared at inner block.outer variable shadowed.'
console.log(`6:${x}`) // sixth
}
}
このサンプルをnode.jsで実行します。
このコードで最初に出力されるのは'1:this variable declared at global.'です。13行目のconsole.logで出力されるのは1行目で宣言と初期化されたもので、これはグローバルスコープ(プログラム内で最も外側のスコープ)になります。
次に出力されるのはlocalLevel1関数内の9行目のconsole.logです。ここで参照されるxは、同一スコープ内にxの宣言がありません。このスコープの外側には1行目で宣言されたxがあるので、これを使用します。結果、'2:this variable declared at global.'が出力されます。
3番目に出力されるのはlocalLevel2関数内の6行目のconsole.logです。ここで参照されるxは、同一スコープ内の5行目にxの宣言があります。このxが使われて、結果、'3:this variable declared at localLevel2.global shadowed.'が出力されます。グローバルスコープのxがローカルスコープのxによってシャドウイングされています。
4番目の例は関数ではないローカルスコープの例で、波括弧(なみかっこ、ブレース)で囲われた部分になります。波括弧で囲った部分はブロックと呼ばれ、関数と同様にローカルスコープを持ちます。18行目のconsole.logでは17行目で宣言したxが使われ、'4:this variable declared at block.global shadowed.'が出力されます。グローバルスコープのxがシャドウイングされています。
5番目の例はブロックを更に入れ子にした場合です。このブロック内のスコープではxの宣言がないので、このスコープの外側でxが宣言されていないか調べます。17行目にxが宣言されているので、このxを使用してconsole.logが実行され'5:this variable declared at block.global shadowed.'が出力されます。グローバルスコープのxがシャドウイングされています。
6番目の例は入れ子のブロック内のスコープでxを宣言した場合です。26行目のconsole.logで参照するxは同一ブロックの25行目で宣言されたxが使用され、'6:this variable declared at inner block.outer variable shadowed.'が出力されます。外側のブロックのxがシャドウイングされています。
基本的なデータ構造
ここでは、コンピュータプログラムを作成するにあたり一般的に使われるデータ構造3つについて説明します。 JavaScriptの型や標準組み込みオブジェクトとしてのサポートはありません。 しかし、これらの考え方を応用する機会は多く、必須の知識と言えるものです。
リスト
リストはJavaScriptの配列に近いデータ構造です。個々の要素に順序があり、ある要素には次の要素への参照データが含まれます。参照データを手繰っていくことで、順番にデータを参照できます。JavaScriptの配列には次の要素への参照データは含まれませんが、インデックスに1を加算することで次の要素を参照できます。
キュー
キューは待ち行列やFIFO(First In First Out)とも言われるデータ構造です。特定の実装のリストで、データの挿入は末尾から行い、データの取り出しは先頭から行うのが原則となります。待ち行列という別名が表している通り、何等かの処理をしたいという要求を行列の末尾につけて、(先に待ち行列についていた)先頭から順に処理するといった時によく使われます。末尾へのデータ挿入、先頭からのデータの取り出しはどちらもJavaScriptのArrayのメソッド(push,shift)として利用できます。
スタック
スタックはFILO(First In Last Out)とも言われるデータ構造です。特定の実装のリストで、データの挿入は末尾から行い、データの取り出しも末尾から行うのが原則となります。末尾へのデータ挿入、末尾からのデータの取り出しはどちらもJavaScriptのArrayのメソッド(push,pop)として利用できます。
内部表現
文字列
UTF-16で表現された文字の並びです。1文字2Byte(16bit)で表現します。UTF-16にはサロゲートペアという仕組みがあり、特殊な文字(絵文字など)では4Byteで1文字を表現するケースがあります。文字数とByte数は単純な相互変換ができません。文字数やByte数を扱う必要がある場合にはサロゲートペアへの対応要否をよく確認するようにしてください。
数値
IEEE 754で定義された倍精度浮動小数点数です。64bitで表現します。内訳は以下の通りです。
- 符号 1bit
- 指数部 11bit(bias=1023)
- 仮数部 52bit
整数と浮動小数点数の区別はありません。どちらも内部的には倍精度浮動小数点数です。
バイナリデータの取り扱い
プログラムで文字列ではない、例えば画像や音声のようなバイナリデータをデータ連携する場合にはバイナリデータのまま扱えない場合があります。このような場合は文字として表現できるように符号化・復号を行います。代表的な例としてBase64とURLencodeについて説明します。
Base64
Base64によるバイナリデータの符号化・複合は最も一般的で汎用的なバイナリデータの符号化・複合方式の一つです。XMLにバイナリのデータを格納する場合には、Base64による符号化がよく使われます。符号化する時はバイナリデータを6bitずつに区切り、48パターンにASCII文字を割り当てて変換します。base64の変換を行うコマンドやツール類も豊富です。
以下にJavaScriptで符号化・複合するサンプルを示します。
let base64String = btoa('sample string')
console.log(base64String)
let decodedString = atob(base64String)
console.log(decodedString)
URLencode
パーセントエンコーディングとも呼ばれる符号化・復号方式です。URIを表す文字列を編集する際に、使用できない文字を表現する為に使われます。バイナリ値を16進数化して%xxの形の並びとして表現します。
以下にJavaScriptで符号化・複合するサンプルを示します。
let encodedString = encodeURI('サンプルです')
console.log(encodedString)
let decodedString = decodeURI(encodedString)
console.log(decodedString)
データの圧縮・展開
大きなデータを扱っていると、外部とデータの授受を行う際に転送に時間やお金がかかってしまいます。これを抑制する為にデータを圧縮し、必要な時に展開して使用するケースがあります。データの圧縮と展開に使用できるライブラリとその使い方を紹介します。
ライブラリとしてdeflate-jsを使用します。これはJavaScriptで使用できるパッケージマネージャ、npmで利用できるパッケージです。以下のコマンドを実行してください。
npm install deflate-js
上のような出力がされたら、インストールできています。
deflate-jsの詳細については以下のサイトをご確認ください。
https://www.npmjs.com/package/deflate-js
サンプルとして、deflate-jsのソースコードを読み込んで圧縮・展開し、圧縮前、圧縮後、展開後のサイズを出力するプログラムを示します。
import deflate from 'deflate-js'
import fs from 'fs'
fs.readFile("./node_modules/deflate-js/lib/rawdeflate.js", "utf-8", (err, data) => {
if (err) {
throw err
}
const original = Array.prototype.map.call(data, function(char) {
return char.charCodeAt(0)
});
console.log(`original:${original.length} bytes`)
const deflated = deflate.deflate(original)
console.log(`deflated:${deflated.length} bytes`)
const inflated = deflate.inflate(deflated)
console.log(`inflated:${inflated.length} bytes`)
})
node.jsで実行すると以下の出力が得られます。
圧縮前と展開後のサイズが等しいので圧縮と展開が期待通りに機能しています。圧縮後のサイズは圧縮前より小さくなっていて、確かにデータが圧縮されていることがわかります。圧縮後は圧縮前のサイズの28%程にできました。

Lesson 1
Chapter 4
利用者との対話
コンピュータプログラムのほとんどは、利用者と対話する為に何らかのインタフェースを持っています。JavaScriptの場合はFront機能としてWebブラウザを通じて利用者と対話するケースと、node.js上のAPIやバックエンド機能としてconsoleを通じて対話するケースがあります。それぞれのケースで、利用者からの入力を読み取る方法、利用者に情報を出力する方法を説明します。
Webブラウザ(DOM操作、HTML/CSS)
Webブラウザ上で動作するFront機能ではHTMLを通じて利用者とデータの授受を行います。CSSはHTML文書の見た目をコントロールする為に使います。
DOMとは
DOM(Document Object Model)とはJavaScriptがHTML文書のデータにアクセスする為のAPIです。このAPIを通じて利用者が入力したものを読み取り、HTMLを書き換えて利用者に処理の結果を伝えます。
要素ノードの取得
documentオブジェクトのgetElementsByXXメソッドを使用して要素ノードを取得します。
個々のメソッドのサンプルを示しますが、次のHTMLに対する操作を行うものとします。
<body>
<p id="paragraph-1">This is paragraph-1.</p>
<p class="paragraph-2">This is paragraph-2.</p>
<p name="paragraph-3">This is paragraph-3.</p>
</body>
では、個々のメソッドの機能とサンプルについて以下に示します。
- getElementsByClassName
このメソッドは引数にクラス名を指定すると、指定したクラスを属性として持つ要素をHTMLCollectionオブジェクトで返します。これはElementsオブジェクトの集合で、配列のように扱えます。HTMLCollectionオブジェクトは元のドキュメントが更新された場合に同じように更新される点に注意して下さい。
document.getElementsByClassName('paragraph-2')
このメソッドは引数にid属性を指定すると、指定したid属性を持つ要素をElementsオブジェクトで返します。id属性は一つのHTML文書内で一意でなければならない為、返り値はコレクションではなくElementsとなります。
document.getElementById('paragraph-1')
このメソッドは引数にname属性を指定すると、指定したname属性を持つ要素をNodeListオブジェクトで返します。これはNodeオブジェクトの集合で、配列のように扱えます。NodeListオブジェクトは元のドキュメントが更新された場合に同じように更新される点に注意してください。
document.getElementsByName('paragraph-3')
このメソッドは引数にタグ名を指定すると、指定したタグの要素をHTMLCollectionオブジェクトで返します。これはElementsオブジェクトの集合で、配列のように扱えます。並び順はHTML上出現する順番です。HTMLCollectionオブジェクトは元のドキュメントが更新された場合に同じように更新される点に注意してください。
document.getElementsByTagName('p')
属性値やテキストの取得と設定
DOMを使い、HTML文書から必要な要素をElementsオブジェクトとして取得することができました。要素には属性値や値としてタグで囲まれたテキストが含まれています。これらを取得したり、任意の値を設定する方法を説明します。
const element = document.getElementById('paragraph-1')
console.log(element.id)
console.log(element.innerText)
element.id = 'updated'
element.innerText = 'this paragraph updated'
最初にgetElementByIdでidがparagraph-1の要素を取得します。値の参照は要素の入った変数に続けてドットと属性名を書きます。console.logでid属性とinnerText属性を出力しています。値の設定は属性に対する代入文を書けば可能です。
実際にコンソールで実行してみます。
取得したelementに対する操作が出来ている事がわかります。idとinnerTextを更新すると、実際にHTMLが更新されます。コードを実行後にブラウザでHTMLのソースコードを確認してください。但し、これはメモリ上で操作されていて、ファイルは更新されない点に注意してください。
不特定の属性の取得
Elementオブジェクトのattributesプロパティを使うと、要素が持っている属性のコレクションが取得できます。あらかじめ属性名がわかっていない場合は、この方法で属性の操作ができます。
const element = document.getElementById('paragraph-1')
const a = element.attributes
for (i=0;i<a.length;i++) {
console.log(a[i])
}
nameプロパティで属性名、valueプロパティで値が取得できます。
入力ボックス・選択ボックスの値の取得・設定
HTMLで定義した入力ボックス、選択ボックスから値を取得したり、任意の値を設定する方法を説明します。
例として次のHTMLがあるものとします。
<h2>input type="text"</h2>
<input type="text" id="inputText">
<h2>select</h2>
<select id="selection">
<option value="1">value 1</option>
<option value="2">value 2</option>
<option value="3">value 3</option>
</select>
サンプルコードを実行する前に、入力フィールドに値を入れておきます。
const inputText = document.getElementById('inputText')
console.log(inputText.value)
inputText.value = 'This is input text field.'
実際にコンソールで実行してみます。
ブラウザに表示されている値も更新されています。
チェックボックスの値の取得・設定
HTMLで定義したチェックボックスから値を取得したり、任意の値を設定する方法を説明します。
例として次のHTMLがあるものとします。
<h2>input type="checkbox"</h2>
<input type="checkbox" id="inputCheckbox" name="inputCheckbox" value="Checked">
const inputCheckbox = document.getElementById('inputCheckbox')
console.log(inputCheckbox.checked)
inputCheckbox.checked = true
checkedプロパティを使うとチェックボックスの状態が取得・設定できます。チェック状態の場合はtrue。アンチェック状態の場合はfalseです。
実際にコンソールで実行してみます。
ブラウザに表示されている値も更新されています。
ラジオボタンの値の取得・設定
HTMLで定義したラジオボタンから値を取得したり、任意の値を設定する方法を説明します。
例として次のHTMLがあるものとします。
<h2>input type="radio"</h2>
<input type="radio" name="radioExample" value="1">
<input type="radio" name="radioExample" value="2">
<input type="radio" name="radioExample" value="3">
ブラウザには次のように表示されます。
const inputRadio = document.getElementsByName('radioExample')
inputRadio.forEach(x => {
if (x.checked) console.log(x.value)
})
inputRadio[2].checked = true
checkedプロパティを使うとラジオボタンの状態が取得・設定できます。チェック状態の場合はtrue。アンチェック状態の場合はfalseです。ラジオボタンは複数の要素の配列のように扱います。個々の要素の中でcheckedとなっているものを探して、与えられたvalueを取り出しています。
実際にコンソールで実行してみます。実行前に、ラジオボタンの左端の要素をクリックしておいてください。
inputRadio[2].checkedをtrueにしたことで、ブラウザに表示されている値も更新されています。
ノードの作成
HTML上に新たなノード(要素)を追加する操作をDOMを通じて行います。以下のHTMLがあるものとします。
<h2>fruit list</h2>
<ul id = "fruits">
<li>banana</li>
<li>cherry</li>
</ul>
ブラウザには次のように表示されます。
このフルーツのリストに新しい要素を作成して追加する操作を行うコードを示します。
const newElement = document.createElement('li')
const newContent = document.createTextNode('durian')
newElement.append(newContent)
const fruitsParent = document.getElementById('fruits')
fruitsParent.append(newElement)
createElementメソッドでli要素を作成し、createTextNodeメソッドで文字列ノードを作成します。作成したli要素に対して文字列ノードをappendすると、durianという文字列を持ったli要素ができあがります。この要素をul要素に対してappendすることで、新たに作成したli要素がリストの末尾に追加されます。
ブラウザに表示されているリストが更新されています。
次に先頭への要素の追加を行うコードを説明します。
const newElement = document.createElement('li')
const newContent = document.createTextNode('apple')
newElement.append(newContent)
const fruitsParent = document.getElementById('fruits')
fruitsParent.prepend(newElement)
最後の命令がfruitsParent.prependに変わっています。prependメソッドを使うと末尾ではなく先頭に要素を追加します。
ブラウザに表示されているリストが更新されています。
ノードの置換
HTML上のノード(要素)を置換する操作をDOMを通じて行います。以下のHTMLがあるものとします。
<h2>fruit list</h2>
<ul id = "fruits">
<li>banana</li>
<li>cherry</li>
</ul>
ブラウザには次のように表示されます。
このフルーツのリストbananaをblueberryに置換するコードを示します。
const newElement = document.createElement('li')
const newContent = document.createTextNode('blueberry')
newElement.append(newContent)
const fruitsParent = document.getElementById('fruits')
const oldElement = fruitsParent.childNodes[1]
fruitsParent.replaceChild(newElement,oldElement)
createElementとcreateTextNodeで新しいblueberryという要素を作っています。oldElementとして、bananaと表示されている要素を取得して、ul要素に対してreplaceChildメソッドを呼び出しています。これはbananaと表示しているli要素を新しく作ったbuleberryというli要素で置き換える処理を行います。replaceChildメソッドの返り値は、置換された要素です。実行結果としてbananaのli要素が返ってきています。
ブラウザを見るとbananaがbluberryに置き換えられています。
ノードの削除
HTML上のノード(要素)を削除する操作をDOMを通じて行います。以下のHTMLがあるものとします。
<h2>fruit list</h2>
<ul id = "fruits">
<li>banana</li>
<li>cherry</li>
</ul>
ブラウザには次のように表示されます。
このフルーツのリストからcherryを削除するコードを示します。
const fruitsParent = document.getElementById('fruits')
const targetElement = fruitsParent.childNodes[3]
targetElement.remove()
削除の対象となる要素を選択して、removeメソッドを実行すると、要素が削除されます。例ではtargetElementに削除対象の要素を取得して、removeメソッドを実行しています。
ブラウザを見るとcherryが削除されています。
インラインスタイル(CSS)へのアクセス
スタイルを設定したい要素のstyle属性を使用すると、直接要素のCSSを参照、設定できます。次のHTMLがあるものとします。
<html>
<head>
<title>css operation example</title>
</head>
<body>
<h2>Gettysburg Address</h2>
<p id="paragraph-1">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.
</p>
<p id="paragraph-2">Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure.</p>
<p id="paragraph-3">We are met on a great battle-field of that war.</p>
</body>
</html>
ブラウザには次のように表示されます。
このp要素のうち、最初のもののフォントを変更するコードを示します。
const targetElement = document.getElementById('paragraph-1')
targetElement.style = 'font-size: 25px;'
getElementByIdで対象となる要素を選び、style属性にcssを代入しています。実行すると、最初のp要素の範囲の文字が大きくなります。
ブラウザを見ると指定の範囲だけ文字が大きくなっていることがわかります。有効なcssであれば任意のものがstyle属性に適用できます。
外部のスタイルシート(CSS)の適用
外部のスタイルシートがHTMLで読み込まれている前提で、読み込まれたスタイルシートに定義されたスタイルを適用する方法を説明します。外部のスタイルシートに定義されたセレクタにマッチするように属性を操作します。ここではclass属性を使用して説明します。
例として次のHTMLとCSSがあるものとします。
<html>
<head>
<title>css operation example</title>
<link rel="stylesheet" href="external-css-example.css">
</head>
<body>
<h2>Gettysburg Address</h2>
<p id="paragraph-1">Four score and seven years ago our fathers brought forth on this continent, a new nation, conceived in Liberty, and dedicated to the proposition that all men are created equal.
</p>
<p id="paragraph-2">Now we are engaged in a great civil war, testing whether that nation, or any nation so conceived and so dedicated, can long endure.</p>
<p id="paragraph-3">We are met on a great battle-field of that war.</p>
</body>
</html>
.sample-style {
font-size: 50px;
font-family: 'Franklin Gothic Medium', 'Arial Narrow', Arial, sans-serif;
}
HTMLに含まれるp要素のうち、最後のものにcssを適用してフォントを変更するコードを示します。
const targetElement = document.getElementById('paragraph-3')
targetElement.classList.add('sample-style')
getElementByIdで対象となる要素を選び、classListにsample-styleを追加しています。実行すると、最後のp要素の範囲の文字が大きくなります。
ブラウザを見ると指定の範囲だけ文字が大きくなっていることがわかります。属性のclassを操作するにはclassList.addとclassList.removeを使用してください。
コンソール入出力
サーバサイドで動くJavaScriptの場合、Frontのようにブラウザを通じたインタフェースを持ちません。コンソールを通じて入力を受け取り、コンソールに出力する方法を説明します。
コンソール入力
readlineモジュールを使うことでコンソールから入力した文字をJavaScriptのプログラムで受け取れます。
以下に、コンソールからの入力をEnterキー押下毎に受け取って'>'を付加してコンソールに出力する例を示します。
import readline from 'readline'
let reader = readline.createInterface({
input: process.stdin
})
reader.on('line', input => {
console.log(`>${input}`)
})
reader.on('close', () => {
reader.close()
process.exit()
})
コンソール出力
コンソール出力はサーバサイドであってもconsole.logで出力できます。特別な考慮はいりません。
以下にコンソール出力の例を示します。
console.log('Hello JavaScript World!')

Lesson 1
Chapter 5
データストアの操作
プログラムで処理した結果は、次回実行時に処理を継続する為に永続的に保管できる場所に保存しておきます。処理の再開・継続が不要な場合は保存する必要ありません。データを保管する場所の事をデータストアと呼びます。JavaScriptで扱えるデータストアには様々な種類があります。ここでは代表的な4種類について説明します。
セッションストレージ
セッションストレージはブラウザがページを開いている間有効なデータストアです。ブラウザを閉じて、もう一度同じページを開いた場合、閉じる前に保存していた値は復元できません。セッションストレージを使うにはsessionStorageオブジェクトを使います。以下のメソッド、フィールドが実装されています。
- clear
- getItem
- length
- removeItem
- setItem
ストレージに入っているデータを全て破棄します。
キーを指定して、対応する値を取得します。指定したキーに該当するデータがない場合はnullが返ります。
ストレージに入っているデータの数を取得します。
キーを指定して、対応する値を削除します。
キーを指定して、対応する値を保存します。
セッションストレージを使うサンプルを以下に示します。
sessionStorage.setItem('key', 'VALUE')
let value = sessionStorage.getItem('key')
console.log(`got value is '${value}'`)
sessionStorage.removeItem('key')
value = sessionStorage.getItem('key')
実際にコンソールで実行してみます。
まず、setItemメソッドで'VALUE'という値を'key'というキーで保存します。読み取るときにはこの'key'を指定します。 setItemで保存した値はgetItemメソッドで読み取ります。保存した時に使ったキーを指定すると対応する値が返り値で得られます。 removeItemメソッドはキーに対応する値が保存されていた場合に、該当する値を削除します。値を削除した後、getItemメソッドで読み取るとnullが返ってきます。
ローカルストレージ
ローカルストレージは保管期限のないセッションストレージです。ブラウザを閉じてもデータは破棄されません。ローカルストレージを使うにはlocalStorageというオブジェクトをつかます。使用可能なメソッド、フィールドはセッションストレージと同じです。
localStorage.setItem('key', 'VALUE')
let value = localStorage.getItem('key')
console.log(`got value is '${value}'`)
localStorage.removeItem('key')
value = localStorage.getItem('key')
localStorageのふるまいは保管期限以外sessionStorageと同じです。
実際にコンソールで実行してみます。
sessionStorageの場合と同じ結果となりました。
ファイル入出力
fileAPI
Front側にはローカルファイルにアクセスする仕組みとしてfileAPIがあります。 inputタグでtype="file"と指定された要素を使ってファイルにアクセスできます。 アクセスする時にはFileオブジェクトを使います。
以下のHTML要素が定義されているものとします。
<input type="file" id="inputfile" multiple>
上のinput要素でファイルが選択されていれば、以下のコードでFileオブジェクトが取得できます。Fileオブジェクトの代表的な属性を取得、表示するサンプルを示します。
const inputFile = document.getElementById('inputfile').files[0]
console.log(inputFile.type)
console.log(inputFile.size)
console.log(inputFile.name)
console.log(inputFile.lastModified)
実際にコンソールで実行してみます。コンソールでコードを実行する前に、ファイル選択ボタンを押してファイルを選択しておいてください。
ファイル選択ボタンで選んだファイルの以下の属性を表示しています。
- type
- size
- name
- lastModified
ファイルの種類を表すMIMEタイプを表示します。
ファイルの大きさを表示します。単位はByteです。
ファイルの名前を表示します。パス名はつきません。
ファイルの最終更新時刻を表示します。単位はミリ秒で、1970年1月1日からの経過時間で表現されています。
ファイルを読み取るサンプルを示します。
const inputFile = document.getElementById('inputfile').files[0]
const inputStream = inputFile.stream()
const reader = inputStream.getReader()
reader.read().then(function processReadValue({done,value}) {
if (done) {
console.log('----- done -----')
return
}
console.log(value)
return reader.read().then(processReadValue)
})
inputFileからstreamを取得し、streamからreaderを取得しています。readerのreadメソッドは非同期の処理です。readメソッドは結果値としてPromiseを返すのでthenを書き、完了時のコールバック関数を記述します。
コールバック関数はdoneとvalueを引数として受け取ります。doneは読み取りが終わったかどうかを表す真偽値が入ります。valueは読み取った結果でUInt8Array(符号なし8bit整数の配列)オブジェクトが返ります。valueの大きさは不定です。
ファイル全体を処理するには、doneがtrueとなるまでreadメソッドを繰り返さなければなりません。その為に、コールバック関数ではdoneがfalseの場合に、readメソッドを再帰的に呼び出します。
fsモジュール
node.jsにはファイルにアクセスする仕組みとしてfsモジュールがあります。WebブラウザのfileAPIではコールバック関数を使って結果を受け取りましたが、fsモジュールではコールバック関数のほかに、関数の結果値として読み取り結果を返す方法もあります。
次の例はコールバック関数を使った例です。エラーが発生するとerrが編集され、エラーが発生しなければvalueに読み取り結果が入ります。
const fs = require("fs")
fs.readFile("node-fs-async-example.js", (err, value) => {
if (err) {
console.log(err)
} else {
console.log(value)
}
})
こちらは関数の結果値として読み取り結果を受け取る例です。
const fs = require("fs")
try {
const value = fs.readFileSync("node-fs-sync-example.js")
console.log(value)
} catch (err) {
console.log(err)
}
関数の結果値として読み取り結果を受け取る書き方は、命令が書いてある順番で実行されるのでコールバックを使った方法よりもコードの可読性が高いです。しかし、読み取りが完了するまで他の処理が実行できない為、実行効率の面ではコールバックを使った方法よりも劣ります。どちらを使うべきかは要件により変わります。要件に対してより適切なものを選んでください。
データベース操作
APIやバックエンドでは、データストアとしてデータベースを利用することが多くなります。ここでは例としてWeb系システムでよく利用されるMySQLを取り上げます。
なお、Frontでは直接データベースを操作する機会はありません。サーバのAPIを通じて必要なデータにアクセスすることになります。
ここではSQLやMySQLの詳細については述べません。JavaScriptを使ってデータベースアクセスする方法について説明します。
mysqlモジュール
ライブラリとしてmysqlを使用します。これはJavaScriptで利用できるパッケージマネージャ、npmで利用できるパッケージです。以下のコマンドを実行してください。
npm install mysql
コマンドを実行すると、次のように出力されます。
mysqlモジュールを使ってMySQLデータベースにアクセスするサンプルを示します。コードを実際に使うには、以下の接続情報を確認してコードに反映してください。
- host: 接続先DBサーバのfqdn名もしくはIPアドレス。例ではlocalhost。
- user: 接続先DBサーバにログインする為のユーザ名。例ではdbuser。
- password: 接続先DBサーバにログインする為のパスワード。例ではdbuserpass。
- database: 接続先DBサーバのデータベース名。例ではsampleDB。
const mysql = require('mysql')
const connection = mysql.createConnection({
host: 'localhost',
user: 'dbuser',
password: 'dbuserpass',
database: 'sampleDB'
})
connection.connect((err) => {
if (err) {
console.log('error on connect to database: ' + err)
} else {
let sqlQuery = 'SELECT * FROM sampleTable'
connection.query(sqlQuery, (err, results) => {
console.log('query result:' + results)
})
}
});
まず、require('mysql')で、MySQLへアクセスするオブジェクトを作成します。次にcreateConnectionでコネクションオブジェクトを作成します。この時接続情報が必要になります。コネクションオブジェクトが作成できたらconnectメソッドを実行します。ここでデータベースへ接続します。接続情報の間違いがあったり、データベースサーバが起動していない場合にはエラーが発生します。接続がうまくいった場合には、queryメソッドを使ってデータベースの操作を実行します。
connection.connectとconnection.queryはそれぞれコールバック関数が必要です。そしてこれが入れ子になっている点に気を付けてください。続けて命令を書いても、記述した順番には動作しません。
ここで注目すべき点はsqlQuery変数に編集している'SELECT * FROM sampleTable'です。これはデータベースを操作する事に特化したSQLという言語です。MySQLをはじめとするリレーショナル型データベースを操作する場合はSQLを使用します。
SQLを使ったデータ操作には以下の命令を使います。
- INSERT
- SELECT
- UPDATE
- DELETE
INSERT命令は新たなデータを作成します。
SELECT命令はデータを読み取ります。
UPDATE命令はデータを更新します。
DELETE命令はデータを削除します。
非同期アクセスとデータの一貫性
データストアに対して書き込み、削除、値の更新が生じる操作を行う場合、意図した通りの順番で行われる事を担保しなければなりません。データストアに対する非同期アクセスに対する考慮のない仕組みは、データの一貫性が保たれません。データの一貫性が保たれないという事は、そのデータはあてにならないという事です。ここではデータの一貫性が保たれないと何が起こるのか、データに一貫性を持たせるためにどのような考慮が必要になるかを説明します。
座席の予約を例としてデータの一貫性について説明します。以下の手順で操作が行われるものとします。
- 空席の一覧をデータストアから取得する
- 空席がない場合はエラーとする
- 空席をひとつ減らし、利用者に表示
- 利用者が確認したら、空席の一覧をデータストアに保存する
一見、何の問題もないように見えます。しかし、座席の予約のような機能は同時に多くの人が利用します。すると次のような事が起こりえます。
- [利用者A]空席の一覧をデータストアから取得する(座席1,2,3が空席)
- [利用者A]空席がない場合はエラーとする
- [利用者A]空席をひとつ減らし、利用者に表示(座席1を表示)
- [利用者B]空席の一覧をデータストアから取得する(データストアがまだ更新されていないので座席1,2,3が空席)
- [利用者A]利用者が確認したら、空席の一覧をデータストアに保存する(座席2,3が空席)
- [利用者B]空席がない場合はエラーとする
- [利用者B]空席をひとつ減らし、利用者に表示(利用者Aと同じ座席1を表示)
- [利用者B]利用者が確認したら、空席の一覧をデータストアに保存する(座席2,3が空席)
最終的に利用者Aと利用者Bに座席1を予約させてしまいました。これがデータの一貫性が保たれていない状態です。このような事が起こらない仕組み、データの一貫性を保つ為の仕組みが必要です。
一貫性を保つ為のシンプルな方法は空席一覧の取得から、空席を減らした後のデータストアの保存までの間に利用者Bが入り込まないようにすることです。
- [利用者A]空席の一覧をデータストアから取得する(座席1,2,3が空席)
- [利用者A]空席がない場合はエラーとする
- [利用者A]空席をひとつ減らし、利用者に表示(座席1を表示)
- [利用者B]空席の一覧をデータストアから取得する(利用者Aの操作が終わっていないので終わるまで待機する)
- [利用者A]利用者が確認したら、空席の一覧をデータストアに保存する(座席2,3が空席)
- [利用者B]空席の一覧が取得できた(座席2,3が空席)
- [利用者B]空席がない場合はエラーとする
- [利用者B]空席をひとつ減らし、利用者に表示(座席2を表示)
- [利用者B]利用者が確認したら、空席の一覧をデータストアに保存する(座席3が空席)
このようなやり方でデータを不測の更新から保護する仕組みを排他制御といいます。データベースではこれをトランザクションという機能で実現していて、簡単に利用できます。
但し、このやり方では利用者Aが座席を確認するまでの間、利用者Bが待たされてしまいます。いつ利用者Bが座席予約ができるかは利用者A次第です。このような仕組みではデータの一貫性は保たれますが多くの人にサービスを提供できません。
データの一貫性を保ちながら利用者を待たせない仕組みがあります。更新する時にデータを取得した時から内容が変わっていないかを確認することです。
- [利用者A]空席の一覧をデータストアから取得する(座席1,2,3が空席)
- [利用者A]空席がない場合はエラーとする
- [利用者A]空席をひとつ減らし、利用者に表示(座席1を表示)
- [利用者B]空席の一覧をデータストアから取得する(データストアがまだ更新されていないので座席1,2,3が空席)
- [利用者A]利用者が確認したらデータストアの空席が最初に取得した時から変わっていないか調べ、変わっていたらエラー(変わっていないのでエラーにならない)
- [利用者A]空席の一覧をデータストアに保存する(座席2,3が空席)
- [利用者B]空席がない場合はエラーとする
- [利用者B]空席をひとつ減らし、利用者に表示(利用者Aと同じ座席1を表示)
- [利用者B]利用者が確認したらデータストアの空席が最初に取得した時から変わっていないか調べ、変わっていたらエラー(利用者Aによって空席は更新されているのでエラー)
こちらの仕組みも排他制御の一種です。先に処理を始めた利用者の処理が終わるのを待機する方式を悲観的ロックと呼び、更新時にデータが変わっていないか確認する方式を楽観的ロックと呼びます。どちらの方式もデータベースのトランザクション機能で利用できます。

Lesson 1
Chapter 6
通信
TCP/IP
HTTP/HTTPS
JavaScriptに限らず、外部との通信を一切行わないプログラムはあまりありません。 多くは通信の基本的なルールとしてTCP/IPとHTTP/HTTPSを利用しています。 ここでは通信の基本的な考え方と、よく使われるAPI呼び出しのコードについて説明します。
- HTTPのメソッド
- GETメソッド
- POSTメソッド
- path/query parameter
- cookie
- fetch
サーバとHTTPを使って通信する場合、HTTPのメソッドを使います。 多くのメソッドが定義されていて、意味に応じた使い分けが必要になります。 ここではよく使われるGETメソッドとPOSTメソッドについて説明します。
GETメソッドはリソースを要求するときに使います。つまり 「URLで指定したリソースを渡してください」という意味です。データの受け渡しの方向はサーバからクライアントです。URLで指定されたリソースのデータが渡されます。
POSTメソッドはクライアントからサーバにデータを送信するときに使います。一般的にはHTMLで定義した入力フォームをサーバに送信するときに使われます。
HTTPではクライアントからサーバにリクエストを送るとき、URLという文字列を使います。 一続きの文字列ですが、部分毎に意味があります。 最初のhttp://、もしくはhttps://の部分は通信プロトコルを表します。通信にHTTP/HTTPSを使うという宣言です。 続く/までの文字列はFQDN名と言い、サーバを識別する名前です。この名前をIPアドレスに変換してサーバと通信を行います。 FQDN名の終わりから続く文字列がパス。パスに続けて"?"で区切られた文字列をクエリと言います。
パスとクエリはサーバ側のプログラムへのパラメータになります。 パスの部分をパスパラメータ(path parameter)、クエリの部分をクエリパラメータ(query parameter)と言います。 ASCII文字列はそのまま編集できますが、ASCII文字列以外のデータを編集する場合はURLエンコード方式の変換を行います。
cookieとは、サーバとクライアントの間で共有される一過性のストレージです。 本来HTTPを使った通信は状態を持ちません。 cookieに状態を保存することで様々な状態を維持しています。 例えば、利用者がログイン済みであるかどうかを表す状態をcookieで管理することで、 ログイン済みの利用者が何度もログインを促されることなくサービスを利用できます。
cookieはサーバがクライアントの状態を管理する為の情報など、セキュリティ上重要な値を保持する事があります。このような値は常にセキュリティ上のリスクにさらされています。なので、cookieを保護する為の仕組みがあります。代表的なものがhttpOnly属性、secure属性、sameSite属性です。
httpOnly属性が指定されたcookieはブラウザ上で動作するスクリプトから読み取れなくなります。攻撃者がスクリプトを使ってcookieを盗み取る事を防ぎます。
secure属性が指定されたcookieはプロトコルにHTTPSが指定されていない場合はクライアントに送信されません。HTTPSを使用するとサーバとクライアント間でやりとりされるデータは暗号化されるので、サーバとクライアントの間で盗聴される事を防ぎます。
sameSite属性を使うと異なるサイト間でのcookieの扱いを変えられます。制限なし(None)、緩い制限(Lax)、厳格な制限(Strict)の3つがあります。LaxかStrictを指定することでCSRFと呼ばれる攻撃を防ぎます。
fetchはWebサーバ上のAPIを呼び出す為の仕組みです。呼び出した処理の結果が返ってくるのを待っていると他の処理をブロックしてしまいます。なので、結果が返ってきた時の処理は、その時実行するようにしておく仕組みを使います。この仕組みがPromiseです。非同期の処理を効率的に記述できます。
以下にfetchメソッドの例を示します。
const person = {
name: 'Yamada Taro',
age: 21
}
fetch('https://fetch.example.com/api/person/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(person),
})
.then(response => {
if (response.ok) {
console.log('success:',response.json);
}
throw new Error('response is not ok')
})
.catch((error) => {
console.log('error:', error);
});
このコードは'https://fetch.example.com/api/person/register'というURLのAPIをHTTP POSTメソッドを使って呼び出します。データはpersonオブジェクトをJSON形式で送ります。結果はfetchメソッドが返すPromiseオブジェクトのthenメソッドのコールバック関数で受け取ります。Promiseオブジェクトのcatchメソッドでは例外処理を行います。

Lesson 1
Chapter 7
フレームワークとパッケージマネージャ
サーバサイドJavaScript
node.js
node.jsとはJavaScriptをサーバで動かす仕組みです。JavaScriptは古くはブラウザ上で動くプログラムの為の言語でした。node.jsはブラウザの代わりにJavaScriptで書かれたプログラムを効率的に動かす仕組みです。ネットワークアプリケーションを作る為に設計されていて、大規模なシステムも構築できるように作られています。
node.jsの特徴の一つはシングルスレッドによる非同期処理モデルを採用している事です。代表的なWebサーバであるApache httpdではコネクション毎にスレッドを生成しています。この為、同時接続数に比例してリソースが必要になり、サーバのリソースが同時接続の上限になってしまう問題がありました。node.jsでは全ての接続に対して1つのスレッドで処理を行う為に、同時接続数増加に伴うサーバのリソース枯渇という問題がありません。
JavaScript言語の基本的な考え方として「処理をブロックしない」(Non-Blocking)というものがあります。コールバックやPromiseが使われるのはその考えの現れです。UIを止めないという要件から端を発した考え方ですが、これが現状でのリソース枯渇の問題への一つの答えとしてnode.jsの実装へと結びつきました。
フレームワーク
express.js
express.jsはnode.js上で動作するWebアプリケーションのフレームワークです。フレームワークとは、日本語で言うと枠組みの事です。何かを作るときに一定の決まった形を与えるものです。express.jsで作るアプリケーションプログラムの基本的な構造はあらかじめ決まっています。
express.jsをインストールするには、以下のコマンドをコンソールから入力します。
npm install express
コンソールから上記コマンドを実行した例です。
ではexpress.jsのサンプルアプリケーションを下に示します。ポート80でhttpを待受け、GETに対して'This is express application!'と応答します。起動時にはコンソールに'express application start now.'と表示します。
const express = require('express');
const app = express();
app.get('/', function(req, res) {
res.send('This is express application!');
});
app.listen(8080, function() {
console.log('express application start now.');
});
上のサンプルをexpressapp.jsというファイル名で保存しておいて下さい。保存できたら下のコマンドをコンソールに入力し、サンプルアプリケーションを実行します。
node ./expressapp.js
'express application start now.'がコンソールに出力されました。
次にサンプルを動かしている環境でブラウザで動作を確認します。プロトコルはHTTP、参照するサーバはlocalhost、ポートは8080を指定します。
'This is express application!'がブラウザ上に表示されました。
パッケージマネージャ
npm
JavaScripには再利用可能なコードを部品としてexportする仕組みがあることは説明しました。 npmはこの部品をパッケージとしてサーバで管理し、CLIコマンドでパッケージの導入、更新、削除ができるようにしたものです。 パッケージの管理を行うので、パッケージマネージャと呼ばれます。
npmという言葉は大きく二通りの意味合いで使われます。1つはクライアントで実行するnpmコマンドです。もう一つはパッケージを管理しているサーバサービスです。こちらもnpmと呼ばれます。サーバサービスとしてのnpmはパッケージレジストリとも呼ばれます。パッケージレジストリサービスのURLは以下になります。
https://www.npmjs.com/
パッケージレジストリには開発に必要なパッケージが数多く登録されています。欲しいものがnpmにあるかどうかは上のウェブサイトの検索機能を使って探してください。
npmを使う上で重要な要素として、package.jsonというファイルがあります。このファイルはnpmコマンドを使ってインストールしたパッケージを管理しているファイルです。
package-lock.json。これはインストールしたパッケージが依存しているパッケージを管理する為のファイルです。npmはパッケージの依存チェーンを解決し、自動的に依存するパッケージを導入してくれます。
パッケージをインストールするには、次のnpmコマンドを使います。
npm install
下の例はmysqlモジュールをインストールする例です。
mysqlモジュールインストール後にpackage.jsonは次のようになっています。
{
"dependencies": {
"mysql": "^2.18.1"
}
}

Lesson 1
Chapter 8
ソースコードの編集と管理
Visual Studio Code
Visual Studio Code(以下VSCode)というソースコードエディタが、JavaScriptに限らずソフトウェア開発では広く普及しています。2021年の調査では71%が利用していると回答しています。
VSCodeはWindows版だけでなく、macOS版、Linux版もあり、ほぼ全てのデスクトップ開発環境で利用できます。
特筆すべきは、VSCode自身がJavaScriptによって書かれていて、機能拡張モジュールもJavaScriptによって書けるという点です。2022年現在、多くの機能拡張モジュールがMicrosoftの公式マーケットプレイスで共有されています。
以下は、Windows版のVSCodeを実際に使用している画面です。
代表的な機能を以下に示します。
- シンタックスハイライト
- 自動フォーマット
- リモート開発
- 実行とデバッグ
JavaScriptをはじめ、多くの言語に対応しています。
ルールに基づいて自動的にフォーマットする機能があります。
コンテナやsshサーバに接続してリモートサーバ上のファイルを編集できます。
コードを実行したり、デバッガを使ったデバッグができます。
VSCodeはMicrosoftによって開発され、ソースコードの一部がGitHubで公開されています。全体としてはOSSという位置づけではなく、古くからあるMicrosoft製品のライセンスでリリースされています。ライセンスとダウンロードへのリンクを下に示します。
- ライセンス:https://code.visualstudio.com/license?lang=ja
- ダウンロード:https://code.visualstudio.com/
Git
Gitはソースコードのバージョン管理システムです。ソースファイルがいつ、誰によって、どのように変えられたかを記録します。リポジトリの管理は分散型のアーキテクチャです。クライアントはサーバリポジトリの完全なコピーを持ちます。
ソースファイルの編集を行う場合には、サーバのリポジトリから編集対象のブランチの完全なコピーをローカルにclone(複製)します。ローカル上で編集が終わったらローカル上でcommit(確定)し、コミットした差分をサーバにpush(送信)します。
よく使うコマンドとして、代表的なものを以下に5つ示します。
- clone
- checkout
- branch
- commit
- push
サーバのリポジトリをクライアントにコピーします。
クライアント上で操作中のブランチを変更します。
ブランチの操作を行います。
クライアント上のリポジトリに現在のファイルをコミットします。
クライアント上の最新コミットををサーバ上のリポジトリにコピーします。
CLIが主に利用されますが、Tortoise GitやSourcetreeなどのGUIフロントエンドと組み合わせても利用できます。VSCodeは標準でGitをサポートしていて、簡易なGUIフロントエンドとしても使用できます。
ブランチの運用には様々な考え方があります。その一つとしてgit-flowがよく使われています。masterからdevelopを派生させ、developから更に機能単位のブランチを派生させ、確定したらマージしていくという考え方です。
- 参考:https://nvie.com/posts/a-successful-git-branching-model/
GitはOSSであり、MIT licenseが適用されます。
- ライセンス:https://git-scm.com/site
- ダウンロード:https://git-scm.com/

Lesson 1
Chapter 9
その他のツール
curl
curlはURLに対するリクエストを作成・送信する為のツールです。curlそのものはCLIフロントエンドで、処理本体はlibcurlというライブラリで行われます。
curlはクライアントからサーバへのhttpレベルでの接続確認や、API機能の検証などによく使われます。ツール自体はhttp以外の様々なスキームのプロトコルに対応しています。例えばFTP、IMAP、LDAP、SMTP等です。
curlはOSSであり、MIT licenseベースの独自ライセンスが適用されます。
- ライセンス:https://curl.se/docs/copyright.html
- ダウンロード:https://curl.se/download.html
curl CLIを使用してテストAPIサーバにPOSTリクエストを送信する例を下に示します。
curl --insecure -b "__SSID=abc123" -X POST -s -d "personalId=1564422" https://localhost:1443/auth/api/check.php
jq
jqはjsonデータを整形する為のツールです。
curlを使って出力がjsonだった場合に、パイプで出力をjqに流して整形する、という使い方をします。
以下のような機能があります。
- 人の目で見て見やすいフォーマットに変換する
- 条件を指定して配列要素の絞り込み
- 型の変換
jqはOSSであり、CC BY 3.0 licenseが適用されます。
- ライセンス:https://github.com/stedolan/jq/blob/master/COPYING
- ダウンロード:https://github.com/stedolan/jq/
インストールにはyumやapt等のパッケージマネージャの利用が便利です。
使用例を示します。次のようなjsonデータファイル(json-example.json)があるものとします。
[{ "date": "2021-03-28", "symbol": "N225", "currency": "JPY", "open": 28084, "close": 27813, "high": 28084, "low": 27813 },
{ "date": "2021-03-28", "symbol": "TOPIX", "currency": "JPY", "open": 1982, "close": 1962, "high": 1982, "low": 1962 }
]
これをjqで整形出力すると、次のようになります。
OpenSSH
OpenSSHはセキュアなリモート操作に係る各種機能をまとめたツールです。 セキュアなリモートログインやファイル転送機能、セキュアな接続の為の鍵の生成などの機能があります。
サーバへのリモート接続にセキュアな接続が求められている場合に、sshが使われます。
OpenSSHはOpenSSHという名前のCLIコマンドではなく、いくつかの名前の異なるCLIコマンドの集まりです。代表的なものを以下に示します。
- sshd
- ssh
- ssh-keygen
- sftp/scp
ssh接続のサーバサービスです。クライアントに対してリモートシェル機能を提供します。
ssh接続のクライアントです。sshサーバに接続し、リモートシェルを利用します。
公開鍵方式での秘密鍵の生成を行います。生成した秘密鍵から公開鍵、証明書の生成も行います。
ssh接続の上でファイルの交換を行うツールです。FTPSとは異なります。
下はssk-keygenの実行例です。
OpenSSHはOSSであり、BSD licenseが適用されます。Windows版のOpenSSHはOpenSSHオープンソースプロジェクトからフォークされたものです。
- ライセンス:https://www.openssh.com/
- ダウンロード:https://www.openssh.com/ftp.html
Linuxの場合インストールにはyumやapt等のパッケージマネージャの利用が便利です。
Windowsの場合設定を開き、アプリ>アプリと機能>オプション機能を選択し、機能の追加からOpenSSH ClientとOpenSSH Serverをインストールしてください。
Docker
Dockerはコンテナ仮想化の為の機能をまとめたツールの集まりです。
コンテナと仮想マシンの違いについて簡単に説明します。コンテナはアプリケーションをアプリケーションの実行に必要なライブラリで包んだものです。仮想マシンはOSを動かす環境をソフトウェアで作り出したものです。OSを動かすためにハードウェアのエミュレーションを行います。コンテナと仮想マシンは広い意味ではどちらも仮想化の為の技術です。しかし「何を動かすための技術か?」という点で大きな違いがあります。
また、コンテナはOSを実行する必要がない為、その分必要とするリソースが少なくて済むという利点があります。しかしながら、コンテナはホストOSの機能を利用する為、組み合わせの問題が生じるリスクがあります。例えばコンテナ側はカーネルバージョン5系のイメージだが、ホストOSがカーネルバージョン4系という事はありえます。
Dockerでコンテナを作るには、まずDockerfileというファイルにアプリケーション実行環境を構築するコードを記述します。このコードを使い、コンテナを構築します。同じコードからは同じコンテナが作られます。この性質があるので、本番環境の水平分散環境のスケールアウトや、開発環境の面数を増やす作業が確実かつ簡単に実行できます。
Dockerは一部がDocker社のライセンス対象であり、一部はOSSでApache 2.0 licenseとなっています。
- ライセンス:https://www.docker.com/legal/docker-software-end-user-license-agreement/
- ダウンロード:https://www.docker.com/products/docker-desktop/
Redisサーバコンテナを作成するDockerfileの例を示します。 DockerHubからredisのコンテナイメージを取得し、redis.confをあらかじめ作成しておいたものに置き換えます。 コンテナ起動時にはredis-serverコマンドを/usr/local/etc/redis.confを引数に実行します。
FROM redis:latest
COPY ./conf/redis.conf /usr/local/etc/redis.conf
ENTRYPOINT ["redis-server","/usr/local/etc/redis.conf"]
上のDockerfileを使い、コンテナイメージを作成するサンプルを下に示します。

Lesson 1
Chapter 10
インフラストラクチャ
コンテナ
コンテナはアプリケーションをアプリケーションの実行に必要なライブラリで包んだものです。インフラストラクチャとしてのサーバは、物理サーバではなくクラウドベンダーのマネージドサービスを利用することが一般的になりました。マネージドサービスとしてのサーバはこれまでは仮想マシンとしてサービスされてきましたが、これがコンテナに置き換わりつつあります。
コンテナは仮想マシンとは違います。どちらも実行環境を仮想化する、物理的なリソースそのものの代わりを用意する技術ですが、扱う範囲が異なります。コンテナはアプリケーションプロセスを動作させる事を目的としていますが、仮想マシンはOSを動作させる事を目的としています。コンテナの場合OSレイヤはホストOSと共有するため、より少ないリソースで運用が可能です。
一方で、OSレイヤをホストOSと共有することはセキュリティ上のリスクであるという議論もあります。この為、セキュリティ上のリスクを低減する目的で、コンテナ実行環境として仮想マシンレイヤを組み合わせてホストOSとの分離レベルを上げる製品もあります。
コンテナの技術仕様はOpen Container Initiative(以下OCI)によって定められています。コンテナ技術はDockerの実装により普及しました。2015年にOCIが設立され標準仕様が定められるようになりました。標準仕様は大きく以下の3つの仕様からなります。
- Runtime Specification
- Image Format Specification
- Distribution Specification
コンテナを実行する為の仕様です。
https://github.com/opencontainers/runtime-spec
コンテナが参照するファイルシステム、コンテナイメージの仕様です。
https://github.com/opencontainers/image-spec
コンテナイメージ配布の仕様です。
https://github.com/opencontainers/distribution-spec
これらの仕様に従うことで、異なるベンダーの製品間の相互運用性が保たれています。
Open Container Initiative:https://opencontainers.org/
コンテナオーケストレーション
コンテナはアプリケーションプロセスを実行するための環境である事は説明しました。サービスやシステムは一つのコンテナ=アプリケーションプロセスだけでは成り立ちません。複数のコンテナを組み合わせて全体としてサービスやシステムとなるように統合しなければなりません。複数のコンテナを統合し運用を行うことをコンテナオーケストレーションと呼びます。
コンテナオーケストレーションの中でも重要な機能にオートスケールとオートリカバリがあります。オートスケールはコンテナ全体の負荷に応じて、新たな負荷分散ノードとしてのコンテナを立ち上げ、クラスターに参加させる機能です。オートリカバリはヘルスチェックに応答しないコンテナを停止し、新たなコンテナを立ち上げる機能です。どちらも運用において重要な機能です。適切に設定すれば、オーケストレーションツールが自動で行ってくれます。
コンテナオーケストレーションを行うツールを紹介します。
- Kubernetes
- Docker Swarm
2022年現在、コンテナオーケストレーションの事実上の標準となっているのがKubernetesです。GCP、AWSといったクラウドサービスでマネージドコンテナのオーケストレーションツールとしてもKubernetesが使われています。
元はGoogleの内製ツールでしたが、Cloud Native Computing Foundationでメンテナンスされるようになりました。大規模なクラスタの構築、運用が可能です。
コンテナと言えばDockerですが、Docker社のコンテナオーケストレーションツールがDocker Swarmです。Docker社のファミリ製品なので、他のツールとの相互運用性に優れています。

Lesson 1
Chapter 11
イベント処理
イベントリスナー・イベントハンドラーの追加
イベントとはブラウザ上で発生する利用者の操作や何等かの処理が終わった事をプログラムに知らせる為の仕組みです。イベントリスナー(イベントハンドラー)とは、イベントの発生を待受けて処理する関数やオブジェクトの事です。独自のイベントリスナーを作って、利用者による操作に対応した処理ができます。
[イベントリスナーを追加するオブジェクト].addEventlistener([イベントの種類],[イベントリスナ])
例として次のhtmlがあるものとします。
<html>
<head>
<title>getElementByXX example</title>
</head>
<body>
<p id="paragraph-1">This is paragraph-1.</p>
<p class="paragraph-2">This is paragraph-2.</p>
<p name="paragraph-3">This is paragraph-3.</p>
</body>
</html>
上のhtmlをブラウザで開いた上で、以下のサンプルを実行します。
const clickText = function() {
console.log('clicked')
}
const elem = document.getElementById('paragraph-1')
elem.addEventListener('click',clickText)
サンプル実行後に'This is paragraph-1.'のテキストをクリックしてください。コンソールに'clicked'が出力されます。
イベントリスナー・イベントハンドラーの削除
追加に続いて、イベントリスナー(イベントハンドラー)の削除について説明します。任意のイベントリスナーを追加することができますが、イベント処理をやめたくなったときは、イベントリスナーを削除できます。
[イベントリスナーを追加するオブジェクト].removeEventlistener([イベントの種類],[イベントリスナ])
先にイベントリスナーの追加で説明したコードが実行されている前提で、以下のコードを実行します。
elem.removeEventListener('click',clickText)
上のコードを実行した後に'This is paragraph-1.'のテキストをクリックしてください。今度はコンソールに'clicked'は出力されません。
イベントオブジェクト
イベントオブジェクトはイベントハンドラーの引数で渡されます。このを調べる事で発生したイベントがどのようなものなのかを詳細に調べられます。
例として次のhtmlがあるものとします。
<html>
<head>
<title>getElementByXX example</title>
</head>
<body>
<p id="paragraph-1">This is paragraph-1.</p>
<p class="paragraph-2">This is paragraph-2.</p>
<p name="paragraph-3">This is paragraph-3.</p>
</body>
</html>
上のhtmlをブラウザで開いた上で、以下のサンプルを実行します。
const eventHandler = function(event) {
console.log(event)
}
const elem = document.getElementById('paragraph-1')
elem.addEventListener('click',eventHandler)
PointerEventと表示されているはずです。左側の▲をクリックして展開すると、イベントオブジェクトの様々なプロパティを参照できます。

Lesson 1
Chapter 12
セキュリティ
SameSite cookie
SameSite属性を使うと異なるサイト間でのcookieの扱いを変えられます。制限なし(None)、緩い制限(Lax)、厳格な制限(Strict)の3つがあります。古いブラウザではデフォルトの動作として制限なし(None)になっていました。これは、異なるドメインのサイト間でcookieが共有できるという意味です。2022年現在、Chrome、Safari、Firefoxといったブラウザでは、デフォルトで緩い制限(Lax)が適用されます。この場合、異なるドメイン間でcookieは共有されません。
この仕様はCross Site Request Forgery攻撃(以下CSRF)に対する対策の一環として導入されました。他に、サードパーティのcookieによる利用者追跡を拒否するという側面もあります。全体として利用者のセキュリティ向上につながっています。CSRFについては後述します。
オリジン間リソース共有
オリジン間リソース共有(CORS)とは、あるドメイン、例えばexample-a.comというドメインのウェブサイトから配信されたJavaScriptが、example-b.comという別のドメインのAPIをFetch APIやXHRを使って呼び出す事を指します。この例ではexample-a.comとexample-b.comという異なるドメインのリソースがドメインを超えて共有されています。オリジンとはリソースが提供される場所のことで、ドメインと考えてください。
TLS(SSL)
Transport Layer Security(TLS)は、通信をセキュアにする為の仕組みです。古い規格ではSecurity Socket Layer(SSL)と呼ばれていました。HTTPSでの使用が有名ですが、SMTPSやFTPSなどのプロトコルでも使われます。以下の3つの機能があります。
- サーバの認証
- データの暗号化
- データ改ざんの検知
サーバがどこの誰が運用するサーバであるのかを、サーバ証明書から調べることができます。サーバ証明書は認証局を使って有効性を検証できます。
サーバとクライアント間での授受されるデータを暗号化します。暗号化に使う共通鍵は、サーバとクライアント間で安全な方法で交換します。
サーバとクライアント間で授受されるデータの改ざんを検知します。データのハッシュ値を使って改ざんがない事を検証しています。
公開鍵基盤(PKI)
公開鍵基盤は2022年現在、信頼のおけるセキュリティが担保された通信にかかせない技術です。鍵自体の強度は攻撃手法の進化に伴い陳腐化・脆弱化が進みます。しかし、攻撃手法の進化に対応して新しい方式の鍵も発表されるので、新しい方式に対応してセキュリティが保たれるようになっています。
ここでは、公開鍵基盤を支える技術要素として以下の項目について概略を説明します。
- 秘密鍵
- 公開鍵
- 証明書
- 暗号化
- ハッシュ値
- 署名
秘密鍵と公開鍵
秘密鍵は発行者と秘密鍵の利用者のみが知っている秘密の鍵で、この鍵を元に対になる公開鍵を作ります。秘密鍵と公開鍵には特殊な数学的な関係があります。公開鍵を使って暗号化した暗号文を復号できるのは秘密鍵のみで、公開鍵を使っても復号できません。この性質を利用して、秘密鍵Aのオーナーとの間で暗号通信を行う時、秘密鍵Aから作られた公開鍵Aを使って暗号文を作り秘密鍵Aのオーナーに送ます。秘密鍵Aのオーナーは暗号文を秘密鍵Aを使って復号します。
この仕組みが有効なのは、公開鍵に対応する秘密鍵を推定するのに必要な時間が、通信内容が陳腐化するのに十分な時間がかかるからです。秘密鍵の推定=暗号文の解読は十分な時間があれば可能ですが、膨大な計算リソースをかけても数千年以上かかってしまいます。
証明書(X.509)
秘密鍵と公開鍵を使う事で、安全な通信ができることは示しました。ただ、この安全性が担保されるのは、秘密鍵Aのオーナーに送る通信文を公開鍵Aで暗号化した場合だけです。手元にある公開鍵が確かに秘密鍵Aに対応する公開鍵Aであることをが証明できなければ、通信の安全性が担保されません。
手元にある公開鍵が確かに通信先が発行した公開鍵であることを証明するのが、証明書という仕組みです。証明書は、公開鍵Aのオーナが外部の認証局Bに証明書の作成を依頼して作るものです。利用者は認証局Bを信頼する限りにおいて、証明書と公開鍵の真正性を確認できます。
証明書のデータは、簡単に説明すると公開鍵Aを認証局Bが持つ秘密鍵Bで署名したデータと、公開鍵Aをセットにしたものです。検証する側は秘密鍵Bに対応する公開鍵Bを使って署名データが公開鍵Aに対して署名したものである事を確認できます。つまり、手元の公開鍵が確かに秘密鍵Aのオーナーが発行した公開鍵Aであると認証局Bが言っている、という事が確認できます。
ここで、認証局Bは信頼できるのか?という疑問が生じます。この場合、認証局Bの証明書を発行している別の認証局Cを使い、証明書を取得して検証します。この認証局の信頼性を検証する為の上位の認証局への連鎖を証明書チェーンと言います。最終的には最上位の認証局であるルート認証局で証明書チェーンは終わります。ルート証明書の署名データは自分自身で署名した証明書、自己署名証明書になります。
暗号化
公開鍵方式の暗号化には比較的多くの計算が必要になります。このため、平文全体を公開鍵を使って暗号化するのではなく、別途使い捨ての共通鍵を作成し、平文全体を暗号化します。そして、使い捨ての共通鍵を公開鍵を使って暗号化し本文に添えて送信します。受信した側はまず、秘密鍵を使って暗号化文から共通鍵を取り出します。その次に取り出した共通鍵を使って暗号文を復号します。共通鍵は毎回使い捨てにすることで安全性を高めています。
署名とハッシュ値
公開鍵方式の暗号化には比較的多くの計算が必要になる事は説明しました。同じことが秘密鍵を使った署名にも言えます。なので署名を作るときは、署名対象データのハッシュ値を求めてからハッシュ値に対して署名を行います。
一般的な攻撃手法と対策
SQL Injection
SQL Injectionとは、クライアントから送るデータに情報を窃取する為のSQLを混入し、これをサーバ側で実行させてクエリの結果を取得する攻撃です。
クライアントから受け取る文字列を無害化処理するのが対策になります。2022年現在、モダンなWebアプリケーションフレームワークではこの無害化処理が既に組み込まれている事が多いです。
データベースで実行するSQLクエリを文字列としてクライアントから送られてきた文字列をそのまま編集して組み立てているとSQL Injectionに対して脆弱になります。
XSS
Cross Site Scriptingとは悪意のあるスクリプトを善意の第三者のウェブサイトに仕掛けることにより、そこにアクセスしてきた人のクライアント上で悪意のあるスクリプトを実行させる攻撃です。善意の第三者のウェブサイトに悪意のあるスクリプトを仕掛けられないように対策を施す必要があります。
「善意の第三者のウェブサイト」での対策例を示します。 クライアントから受け取る文字列を無害化処理するのが対策になります。具体的には次のような文字に変換を施します。
- <→<
- >→>
- &→<
- "→"
- '→'
- White Space→
2022年現在、モダンなWebアプリケーションフレームワークではこの無害化処理が組み込まれるようになってきています。
CSRF
Cross Site Request Forgeryとは、標的サイトにログインしているユーザから悪意のあるリクエストを送信させる攻撃です。標的サイトで悪意のあるリクエストを排除する対策を施す必要がります。
標的サイトでの対策としては、攻撃者に推測されないトークン情報をリクエストに含ませるのが有効です。標的サイトではトークン情報がサーバで生成・発行したものと同じである事を検証して悪意のあるリクエストかどうかを識別します。トークン情報の生成タイミングは、セッション開始時、ログイン成功時、ページ生成時など様々です。生成したトークン情報は攻撃者に漏洩しない事、推測されない事が大切です。TLSによる通信の暗号化により、経路上でのトークン情報窃取を防ぎます。
また、cookieのSameSite属性やCross Origin Resource Shareingの設定も対策として有効です。

Lesson 1
Chapter 13
非同期処理
コールバック
JavaScript言語の基本的な考え方として「処理をブロックしない」(Non-Blocking)というものがあります。コールバックという仕組みはそれを実現する最も基本的な仕組みです。
I/O処理やサーバとの通信を伴うAPI呼び出し処理を行う場合、処理がいつ終わるか予測がつきません。これを同期的に実行していると他の処理をブロックしてしまいます。フロントの場合はUIの更新が止まり「カクつく」ように見えてしまいます。サーバサイドの場合は処理の性能が上がらなくなった結果、多くのクライアントが応答を待たされてしまいます。このような処理のブロックを防ぐ仕組みの一つがコールバックです。
ここでコールバック処理の実例を示します。XHRでサーバドキュメントを取得し、取得結果を返したところから処理を再開するコードです。
const xhr = new XMLHttpRequest();
xhr.addEventListener("load", function() {
console.log('get google completed.');
});
xhr.open("GET", "https://www.google.co.jp/);
xhr.send();
console.log('get google initiated.');
このコードを実行すると、xhr.send(); でopenで指定したURLへのリクエストが送信されます。そのあと、'get google initiated.'がコンソールに出力され、サーバからのレスポンスを受け取った所でxhrの"load"イベントが発生、コールバック関数が呼び出されて'get google completed.'がコンソールに出力されます。
Promise
Promiseは非同期処理を実装する為の仕組みです。同様の仕組みとしてコールバックがありますが、Promiseはコールバックよりも後に導入されました。コールバックによる非同期処理を改良する仕組みで、可読性を向上します。
Promiseオブジェクトとは
非同期に行いたい処理をラッピングして、処理の完了を非同期に受け取る為のオブジェクトです。処理を行う関数を引数に取り、この関数は処理の成功を通知するresolve関数と、処理の失敗を通知するreject関数を引数にとります。
Promiseオブジェクトの基本
完了までに時間がかかる処理を実装する場合、そのまま同期的に処理を実行すると他の処理をブロックしてしまいます。非同期に処理を行うには、実行したい関数をPromiseでラップし、処理の完了を通知するresolveとrejectの2つの関数を引数で受け取ります。
Promiseオブジェクトを使った非同期処理の実装例を示します。
function getPromise(someParams) {
return new Promise(function asyncProcessFunction(resolve, reject) {
// 非同期に実行したい処理をここに記述します
if (someParams === '*') {
throw new Error('error!');
}
const rem = someParams % 3;
if (rem === 0) {
resolve(rem); // 処理が正常終了したことを通知します。
} else {
reject(rem); // 処理が正常終了しなかったことを通知します。
}
});
}
getPromise関数は、非同期処理を実行するPromiseオブジェクトを返しています。Promiseオブジェクトをインスタンス化する時、Promiseクラスのコンストラクタ引数として非同期に実行したい関数、asyncProcessFunctionを渡しています。asyncProcessFunction関数では処理が正常に終了した場合はresolve関数を呼び出し、正常ではなかった場合にはreject関数を呼び出します。
Promiseオブジェクトの結果取得
次に、このPromiseオブジェクトを使って処理結果を受け取る処理の例を示します。
function onResolved(value) {
const out = `resolved. value="${value}"`;
console.log(out);
return out;
}
function onRejected(value) {
console.log(`rejected. value="${value}"`);
}
getPromise(5)
.then(onResolved)
.catch(onRejected);
この例ではまずonResolved関数とonRejected関数を実装しています。それぞれ正常終了時に呼び出すresolve関数、正常終了ではなかった場合に呼び出すreject関数に対応しています。onResolved関数とonRejected関数の実装が、実際の正常終了時の処理、正常終了ではなかった場合の処理になります。
次にgetPromise関数を呼び出しています。呼び出した結果としてPromiseオブジェクトが返り、続けてthenメソッド、catchメソッドを呼び出しています。thenメソッドでは正常終了時の処理であるonResolved関数を、catchメソッドでは正常終了ではなかった場合の処理であるonRejected関数を引数として渡しています。
Promiseオブジェクトの連結
Promiseオブジェクトを使い、非同期処理の結果をthenとcatchで受けるコードの書き方を説明しました。では、続けて別の処理を開始する場合の記述法について説明します。
thenメソッドで、resolved関数を通じた結果を受け取る関数を実行できますが、このメソッドは結果値として結果を受け取る関数の終了を待つPromiseオブジェクトを返します。結果値がPromiseオブジェクトなのでthenによってその結果を受け取れます。具体的には次のように記述できます。
getPromise(5)
.then(onResolved)
.then(onResolved)
.catch(onRejected);
例のようにメソッドチェーンで処理の続きを記述できます。
Promiseオブジェクトの失敗の処理
Promiseオブジェクトによって非同期に実行した処理が何らかの理由で失敗した場合は、reject関数の呼出によってその結果が通知されます。reject関数に渡した結果はcatchメソッドによって結果を受け取る関数に引き継がれます。これまでに示した例ではonRejected関数が結果を受け取る関数の実装です。
複数のPromiseオブジェクトの実行
Promiseオブジェクトをthenでつないでいく方法について説明しました。thenを使ったメソッドチェーンでは同期的な順序のある処理を実装できます。ここでは、複数のPromiseオブジェクトを非同期的に扱う方法を説明します。
3つのPromiseオブジェクトを生成し、全ての処理の完了を待ち、結果を表示する例を示します。
const promise1 = getPromise(3);
const promise2 = getPromise(6);
const promise3 = getPromise(9);
Promise.all(promise1,promise2,promise3)
.then((values) => {
console.log(values);
})
.catch((values) => {
console.log(values);
});
この例ではpromise1、promise2、promise3の3つのPromiseオブジェクトを扱っています。この3つのPromiseオブジェクトはPromise.allメソッドの引数として渡しています。こうすることにより、3つのPromiseオブジェクトの処理が全て正常終了した時にthenメソッドが呼び出されます。もし、1つでもreject関数が呼ばれた場合はcatchメソッドが実行されます。
Promiseオブジェクトの競争
複数のPromiseオブジェクトの完了待ちを行う場合、全てが完了するのを待つのがallメソッドでした。raceメソッドを使うと、引数で渡したPromiseオブジェクトのうちどれか一つが終了した所でthenメソッドが呼び出されます。終了は正常終了、異常終了を問いません。
async/await
非同期処理を表現する方法としてPromiseがある事を説明してきました。可読性を更に高める事を目的として、async/awaitという構文が導入されています。 本質的にはPromiseと等価ですが、一般的な用途でコードの読みやすさと書きやすさが向上しています。 従来のPromiseも使用できます。
async関数
関数の前に「async」と付けることで、この関数がPromiseを返す非同期処理である事を明示します。この関数は内部で同期処理によるブロックが発生する可能性があります。
async関数の戻り値
async関数はいつ終わるかわからないので、呼び出し元で終了監視する為にPromiseを返します。 async関数内では明示的にPromiseを返す必要はなく、単純にreturn文で値を返すコードを書けます。そうすると返り値をResolveしたPromiseを返す処理に置き換えられます。
awaitとは
awaitはasync関数の内部でのみ使える特別な構文です。awaitが使えるのはPromiseを返す関数だけです。関数呼び出しの前に「await」と書くと、呼び出した関数が返すPromiseがresolveかrejectとなるまで同期的に待機します。

Lesson 1
Chapter 14
現場のルール
命名規則
JavaScriptに限らずプログラミング言語でプログラムコードを記述する時は適切な名前をつける事が常に求められます。代表的なものは変数、関数、クラス、ファイル、フォルダです。名前はそれを見た人に何のために使うものなのか、どのような機能を実装したものなのかを伝えるものです。自分以外の人が読んで理解しやすい名前をつける事が、規則以前の大前提となります。
この章では一般的に使われる規則について説明します。しかし、現場毎に異なるルールが決まっている事があります。それらのルールは理由があって決められています。なぜルールが決まっているのかをよく理解してルールに準じたコードを書くようにしてください。
キャメルケース
複数の英単語を並べて意味の通るわかりやすい名前を作ります。この時、単語と単語の区切りがわかるように単語の先頭のみ大文字にし、続く文字を小文字にします。具体的には次のようになります。
companyName
会社名を表す名前としてCompanyとNameという2つの英単語を組み合わせました。最初のCompanyの最初のCを小文字にするやり方をローワーキャメルケースと言います。最初のCを大文字にするやり方をアッパーキャメルケースと言います。
ケバブケース
英単語を並べるときの規則には単語と単語の間にハイフンを入れるやり方もあります。これをケバブケースと言います。具体的な例を示します。
company-name
CompanyとNameの間をハイフンでつなぎます。ファイル名やフォルダ名によく使われます。
スネークケース
英単語を並べるときの規則には単語と単語の間にアンダーバーを入れるやり方もあります。これをスネークケースと言います。具体的な例を示します。
company_name
CompanyとNameの間をアンダーバーでつなぎます。ケバブケースと同様にファイル名やフォルダ名によく使われます。
動詞+目的語
名前を定義する際に重要なのは、定義するものが何らかの機能を表すのかデータを表すのかの区分です。機能を表す名前をつける場合は動詞+目的語の組み合わせで表現することが一般的です。例として、ある関数を定義してみます。
function getEmployeeDetail() {}
動詞としてGetを使い、目的語としてEmployee Detailを使っています。この3語をローワーキャメルケースで接続して従業員の詳細情報を取得する関数の名前としました。このように3~4語程度の簡単な英単語の組み合わせで、機能そのものを表現した名前が保守性の面で望ましいです。
コーディングスタイル
プログラムコードを記述する事は、プログラムの論理的な構造を文字で表現する事です。ブロック構造のレベルを表現する為にインデンテーション、カッコの閉じ方を共通化しなければなりません。ルールは現場毎に決まりますが、最近は自動的・機械的にルールを適用してプログラムコードを整形するエディタが一般的です。
具体的にはVisual Studio Codeとprettierやeslintの組み合わせでコードの自動整形ができます。この、自動整形をする機能をフォーマッターと言います。コーディングスタイルはフォーマッターの設定で変わります。現場毎に共通の設定があるので、同じ設定を使うようにしてください。
コメントの入れ方
コメントもプログラムコードの重要な一部です。プログラムコードは必ず書いた人の手を離れて第三者の手による保守が行われます。この時プログラムコードの意図をわかりやすく伝える一番効果的な手段がコメントになります。適切なコメントを入れて保守のしやすいプログラムコードを記述してください。 一行、複数行。使い分け。
コメントの入れ方には、大きく二通りの入れ方があります。一行で入れるコメントと、複数行にわたるコメントです。一行で入れる例を示します。
// 四角形の面積を求める
const rectangleArea = height * width
このようにプログラムコードの前にコメント行を入れ、一行で簡潔にコードの意味を説明します。
次に複数行でコメントを入れる例を示します。
/*
四角形の面積を求める
parameters:
height:四角形の高さ
width:四角形の幅
returns:
四角形の面積
*/
function getRectangleArea(height,width) {
return height * widith
}
/*と*/で囲った範囲がコメントとして扱われます。このスタイルのコメントは関数やクラスなどの機能の説明によく使われます。例では関数のパラメータや返り値についてコメントで説明を加えています。このようなコメントを加えると、関数全体のプログラムコードを読まなくてもコメント行を読むだけでどのような関数なのかが分かり、すぐに利用できるようになります。

Lesson 1
Chapter 15
プログラミング的思考
この章はJavaScriptの言語仕様の説明ではありません。プログラムを作るときに必要になる考え方について説明します。この考え方はJavaScriptに限らず、ほとんどの言語によるプログラム作成にも適用できるものです。
手順や手続きの共通点や相違点を整理し、共通の部品にする(抽象化、一般化)
プログラムコードを書く時、良いプログラマが考えるのは「同じコードを書かない」ということです。 JavaScriptには関数という機能をひとまとめにして名前を付ける仕組みがあります。 同じコードを繰り返し実行する為のループ構文があります。
大きな問題を、より小さな問題に分解する(分解)
解決しなければならない問題から直接プログラムコードに落とし込むのが難しい時は、解決したい問題をいくつかのステップに分解します。 次に分解したステップをプログラムコードに落とし込めるか検討します。ここでもプログラムコードに落とし込むことが難しければ、さらにいくつかのステップに分解します。この分解するステップを実際のプログラムコードに落とし込めるまで続けます。
既存の部品を組み合わせて、目的を達成する(組み合わせ)
プログラムを作るっていくと、既に作っている関数やクラスが利用できるケースもよくあります。積極的に使うべきです。そのままでは利用できない関数やクラスも、うまく適合できるように中間の関数やクラスを作る事も有効です。再利用性の高い部品をうまく組み合わせて問題を解決する事ができれば、新しい部品を作るよりも良い方法です。
手順や手続きを客観的に見て、良い点・悪い点を見つけ出す(評価・分析)
これまでに、プログラムを作るのに必要ないくつかのプロセスについて説明をしてきました。
- 抽象化、一般化
- 分解
- 組み合わせ
これらのプロセスは一度やればよいというものではありません。出来上がったプログラムコードの良い点、悪い点を見つけてより良いプログラムになるように見直しをしていきます。良さの基準は様々で、プログラムが解決すべき問題によって異なります。一般的には次のような点が評価すべき観点になります。
- 早く処理が終わること
- 修正がしやすいこと
- 短く簡潔で無駄がないこと
- 安全であること
