[Home] [Top] [Back] [Next] 空間を "隔離" する魔法
6. JS魔法修行: (4) データ操作を学ぶ
6-1. 七つの「型」のこと
6-2. JSの魔法修行

6. JS魔法修行: (4) データ操作を学ぶ

これまでちょっとふざけた感じでプログラミングを学んできました。
しかし時には "真剣になる" ことも大事です。
本チュートリアルの全連載を通して あまり面白味のないトピックが二つ があります。一つが 「データの話」 、もう一つが 「スコープの話」 です。ここではそのうち 「データの話」 をします。
「面白みがない」 とは言っても、この章を終えて、自由にデータが操作ができるようになると、ぼんやりとした見えなかったプログラムの正体がはっきりとした輪郭を垣間見せてくれるはずです。そうすると 現実のウェブ技術たちを魔法のように駆使するための手順 が自分の目で確認できるため、そこから一気にこの "旅" はエキサイティングな様相を呈してくると思います。

6-1. 七つの「型」のこと

データには "かたち" があります。一般的にはこれを 「型」 といいます。読み方は "かた" です。これはプログラムの最小単位であり、それぞれのプログラミング言語で決められている、誰もが逆らうことのできない、それはそれは厳しいルールです。
データの表現に厳格なルールがなければ、現実世界のモデルをプログラムに正確に置き換えられません。その言語にはどういった 「型」 があって、どうすればそれらを使って現実世界をデータに落とし込み、どのようにそれらを駆使するかを知らなければなりません。
生物学で細胞の構造を知らなければ何もできないし、数学に代数・幾何がなければ始まりません。風水や中国医学にだって 「陰陽」 や 「五行」 という基本原理がありますし、錬金術で 「四大元素」 を知らないなんて話になりません。
とはいえ、JS の 「型」 は 他のプログラミング言語に比べると極端に少ない ほうです。どちらかというと JS の場合はそれを駆使するための 「テクニック = 魔法(?)」 のほうが重要になります。
これから 「七つの型」 を一つずつ紹介していきます。でも説明ばかりだと "眠くなるだけ" でしょうから 具体的なコードから直感的に理解できる ようにしています。またデータを駆使する 「テクニック = 魔法」 についても出来るだけ魅力的な例を選んでみました。
ただし一つ思い出してもらわなければなりません。
"変数を宣言する方法に2種類ある" ということです (これは 「4-3. イベントループ - (c) let と const」 でも簡単に説明しました) 。
let ・・・ あとで 「変更する」 変数を作るときに使う
const ・・・ あとで 「変更しない」 変数を作るときに使う
以下に紹介する例のいずれも letconst をごちゃまぜに使っています。「あとで変更するもの」 については let を使って宣言し、「あとで変更しないもの」 ものは const を使っています。でもこれは 型の特質とは無関係である ということを留意しておきます。

(1) 数値型

const price = 99;
price = price + 1;

console.log('Give me ' + price + ' yen');
// --> Give me 100 yen
これは 99 + 1 = 100 だということですね。
数値は説明がなくても分かるでしょう。
変数を "書き換える" こともできますが、上の例では特に変数を変更する必要がないので const を使って宣言しています。

(2) 文字列型

let condition = 'lovely';
console.log('You look ' + condition + ', Cathy.');
// --> You look lovely, Cathy.

condition = 'dead';
console.log('You look ' + condition + ', Joe.');
// --> You look dead, Joe.
文字列もあまり説明は必要ないでしょう。
lovely'' で囲んでいますが、これは "" で囲んでも問題ありません。
また上記から分かるように、複数の文字列を + で連結することができます。
でも連結する文字列が増えてくると + ばかりでコードが読みづらくなります。
なので、こういうやり方も用意されています。
let condition = 'lovely';
console.log(`You look ${condition}, Cathy.`);
// --> You look lovely, Cathy.

condition = 'dead';
console.log(`You look ${condition}, Joe.`);
// --> You look dead, Joe.
文字列の全体を `` で囲んでいます。そして挿入したい変数を ${} で指定しています。ここでは condition を挿入したいので、${condition} としています。
こうすると一つ目は
You look lovely, Cathy.
になりますし、二つ目は
You look dead, Joe.
になります。
こういう連結の方法は 「テンプレート・リテラル」 といいます。

(3) boolean 型 (論理型)

const price = 1;

let angry = false;
if (price < 100) {
  angry = true; // 100円以下だったら機嫌を損ねます
}

if (angry) {
  console.log(`Only ${price} yen? Give me more!`); // たった xx 円? もっとくれ!
} else {
  console.log('Thanks.');
}
// --> Only 1 yen? Give me more!
一般的には 状態 を表すのに使います。
price の方はずっと値が変わらないので const で宣言しています。angry は途中で値が変わるので let で宣言しています。

(4) 配列型

const girls = ['Cathy', 'Anna', 'Sarah', 'Lucy'];

console.log(girls[0] + ' is lovely.'); // --> Cathy is lovely.
console.log(girls[1] + ' is gorgeous.'); // --> Anna is gorgeous.
console.log(girls[2] + ' is charming.'); // --> Sarah is charming.

girls[2] = 'Joe'; // 書き換える!
console.log(girls[2] + ' looks dead. He is not even a girl.');
// --> Joe looks dead. He is not even a girl.
「配列」 はよく使われます。詳しい使い方はあとで学びます。
ここで覚えておくべきは 「インデックス」 というものです。「配列の何番目を指すのか」 を示すための数字です。一番目のインデックスは 0 から始まります。二番目が 1 です。三番目が 2、、、と続きます。
例えば girls という配列であれば girls[1] というときにインデックス 1 は二番目を意味するので Anna が取り出せます。
あれ?
おかしなことに気づきました。
girls 配列は "書き換えている" のに const で宣言しています。
確かに。でもよく見て下さい。
girls 配列の 要素 は書き換えていますが、 girls 配列それ自体 は書き換えていません。この場合、const で宣言しても問題ありません。

(5) オブジェクト型

const food = {
  pizza: 3,
  beer: 4
};
console.log(food.pizza); // --> 3

// ナチョスを追加してみる
food.nacho = 100;
console.log(food.nacho); // --> 100
「配列」 とおなじく 「オブジェクト」 もよく使われます。むしろプログラムの大半がオブジェクトだと言っていいくらいです。取り出すときは 「. (ドット)」 で繫げます。food.pizza とすればこの場合は 3 が取り出せます。さらにこの例では food.nacho = 100 とすることで nacho という新しい "要素" を food オブジェクトに追加しています。
さて、こちらも const で宣言されていることに気づきましたか?
確かに途中で 要素 を追加しています。
でも先ほど 「配列」 のほうで 「要素だけを書き換える」 だけであれば配列を const で宣言できたのと同じように、「オブジェクト」 についても 「要素だけを書き換える」 だけであれば const で宣言することが許されています。
もう一つの例を見ましょう。
const x = 15;
const y = 30;

const pos = { x: x, y: y };

console.log(`The position is at: (${pos.x}, ${pos.y})`);
// --> The position is at: (15, 30)
これで pos{ x: 15, y: 30 } という形のオブジェクトになります。取り出すときは pos.x といった具合です。
これは次のようにも書けます。
const x = 15;
const y = 30;

const pos = { x, y };

console.log(`The position is at: (${pos.x}, ${pos.y})`);
// --> The position is at: (15, 30)
違いが分かりますか?
これ
const pos = { x: x, y: y };
よりも
const pos = { x, y };
こっちのほうがスッキリしますね。
オブジェクトの中身はどちらも { x: 15, y: 30 } のままです。
「書き方を省略できる」 ということです。

(6) 日付型 (Date 型)

実は 「日付型 (Date 型)」 は本来はここで紹介するものではありません。JS を知っている人たちからは 「えっ、なんでここで?」 と言われるでしょう。Date を紹介するんだったら Uint8Array だとか Symbol とか他ににもあるだろう、突っ込まれるはずです。でもここで Date を紹介するのは よく使われるから です。プログラムが人間のためであるとき、日付と時間はどうしても避けられません。「型」 の一つとしてではなく、頻繁に使われるデータのかたちとして紹介しています。
const party = new Date();

const hour = party.getHours();
const min = party.getMinutes();
const sec = party.getSeconds();

const clockhour = `${hour}:${min}:${sec}`;

console.log(clockhour); // --> 23:59:10
・・・・
・・・・
・・・・
と、これくらいでしょうか。これだけ覚えておけば、結構、 JS で色々なことができます。
おっと!
大事なものを忘れていました。
(わざとらしくてすみません)

(7) 関数型

「関数」 も立派な 「型」 です。
というか JS で一番大事な型です。
function say(s) { console.log(s); }

say('Joe is totally dead.');
// --> Joe is totally dead.
実は JS では 「関数」 を "変数の中に入れる" こともできます。
こんなふうに。
const say = function (s) { console.log(s); }
さらに上記はこんなふうに表記することも可能です。
const say = (s) => { console.log(s); }
表記が違うだけで、やっていることは同じです。
「アロー関数」 と呼ばれます。
短くなるとちょっと嬉しくなるので使います。
厳密には 「アロー関数」 は通常の関数とは振る舞いが異なります。これは次の章 「スコープ」 で説明します。
ちょっとこちらも見て下さい。
const addOne = (n) => {
  return n + 1;
}
say 関数の方はその中で console.log を使ってログ出力しているだけでした。
しかしこの addOne 関数のほうは return という表現を使うことで 「計算した結果を外に返す」 ということをしています。
だから addOne 関数を呼び出すとしたら、こんな感じになります。
const addOne = (n) => {
  return n + 1;
}

const result = addOne(100);
console.log(result); // --> 101
この例の場合、返される結果を result の中に入れています。
いずれにしても重要なのは JS では 「関数を変数として扱える」 ということです。
この魅力については、これから少しずつ触れていきます。

6-2. JSの魔法修行

ではここまで紹介した 「型」 を実際に使ってみます。
一般的なチュートリアルであれば簡単なものから始めて、徐々に難しいことをやるのでしょう。
でもこのチュートリアルでは最初から難しいことをやってみましょう。
しかも JS で一番特殊な 関数 から始めます。
JS に特有な関数の特性を使うと、他のプログラミング言語には真似できない、まるで 魔法のようなこと ができるからです。
「他のプログラミング言語には真似できない」 は言い過ぎす。"関数型" と呼ばれる他の Lisp, Scala, Haskell, OCaml, Clojure, Rust などはむしろ JS よりもアクロバティックで、さらにエキサイティングな言語たちです。

関数の魔法

JS で "関数の魔法" の使い方を学びます。
最後にやったこれ。
"関数" を定義する基本的なやり方はこうです。
function say(s) { console.log(s); }
でもこれは const に入れて、こうも書けると言いました。
const say = function (s) { console.log(s); }
あるいはこうも書けると言いました。
const say = (s) => { console.log(s); }
さっきはお伝えしませんでしたが、実はこれはこうも書けます。
const say = (s) => console.log(s);
こっちはどちらかというと "短くしたいだけ" です。
中身が一行しかないときは {} を省略できるのです。
もっと短くしましょうか。
これはさらにこうも書けます。
const say = s => console.log(s);
引数が一つしかないときは () を省略できるのです。
さて JS は 「関数を変数として扱える」 と言いました。これはどういうことでしょう?
例えばこういうのがあるとします。
const num = 100; // 元のお金は 100円

const addOne = (n) => n + 1; // 1 を足した結果を返す
const addTwo = (n) => n + 2; // 2 を足した結果を返す

const result = addOne(num);
const result2 = addTwo(num);

console.log(result); // --> 101
console.log(result2); // --> 102
コードは理解できますか?
addOneaddTwo という2つの関数は、何をやっているのでしょうか。
addOne は与えられた 1001 を足します。返される 101result に入れています。
addTwo は与えられた 1002 を足します。返される 102result2 に入れています。
addOne に渡しているのは何でしょう? num ですね。これは 数値 です。
addTwo のほうにも "数値" を渡しています。
じゃあ 数値じゃないもの を渡してみましょうか!
まずは新しく autobot という関数を作ってみます。
const num = 100; // 元のお金は 100円

const addOne = (n) => n + 1; // 1 を足した結果を返す
const addTwo = (n) => n + 2; // 2 を足した結果を返す

// 「関数」 と 「数値」 を受け取り、受け取った 「関数」 を実行して返す.
const autobot = (transformer, n) => transformer(n);

const result = autobot(addOne, num); // addOne 関数を渡す (100円も)
const result2 = autobot(addTwo, num); // addTwo 関数を渡す (100円も)

console.log(result); // --> 101
console.log(result2); // --> 102
余談ですが autobot というのは映画 Transformer に出てくる異星人のロボットです。クルマのかたちをしていて、ガッチャン、ガッチャン、"変身 (transform)" するんですね。
autobot には2つの "引数" を渡しています。
第一引数
transformer は "やりたい仕事" です。
なんと! 第一引数には "関数" が渡されています!
第二引数
n は "適用する値" です。
これには "数値" が渡されています。
autobot( やりたい計算,  適用する値 )
result のほうで "やりたい計算" はどうやら addOne のようですね。autobotaddOne という関数を引数として受け取る と、この関数が実行され、返された結果は result に入ります。
result2 のほうで "やりたい計算" は addTwo のようです。autobotaddTwo という関数を引数として受け取る と、この関数が実行され、返された結果は result2 に入ります。
やりたい仕事 (関数) を引数として渡せる のです。
何がすごいのかって?
そうですね、、、、
例えば autobot が現在は "地球上" で仕事をしているとします。
それで "木星" や "火星" に行ことになったら、他のプログラミング言語だったら autobot を書き換え、木星や火星にも対応できるような処理を追加しなきゃなりません。でもさらに他の星に行くことになれば、どんどん if / else 分岐が増えて中身が膨れ上がってしまいます。
一方、関数 (やりたい仕事) を引数として渡せる と、ファミコンのカセット (年齢がバレバレですね) みたいに訪問したい星のための "コンテキスト (文脈)" を与えるだけでよく、本人は相変わらずやるべきことをやるだけで、中身が膨れ上がることがありません。
「関数を変数のように扱える」 ことのスゴさ、何となくイメージがつきましたか?

配列の魔法

次に 「配列」 をやりましょう。
まずはコードを見てみます。
const girls = ['Cathy', 'Anna', 'Sarah', 'Lucy'];

girls.forEach(function (name) { // ぐるぐる回す
  console.log(`${name} is lovely.`);
});
// Cathy is lovely.
// Anna is lovely.
// Sarah is lovely.
// Lucy is lovely.
まずは girls 配列に 「女の子たち」 が定義されています。
そして girls 配列の後ろに forEach というのをくっつけ、何やらやっています。
名前から "察する" のがプログラミングです。
forEach は日本語だと 「一つずつにつき」 です。
girls.forEach であれば 「女の子たち一人ずつにつき」 になります。
つまり forEach「配列の要素をぐるぐる回し、指定された関数を実行」 してくれるものであることが推測できます。
「指定された関数」 ってどれのことでしょうか?
これです。
// この関数が指定されている
function (name) {
  console.log(`${name} is lovely.`);
}
forEach という関数には、その引数として、この関数を指定しています。
おっと!
やっぱり forEach 関数にも "関数" を渡しているのですね!
見やすくするため、関数に名前をつけてましょう。printGirl にしましょうか。
const girls = ['Cathy', 'Anna', 'Sarah', 'Lucy'];

const printGirl = name => console.log(`${name} is lovely.`);

girls.forEach(printGirl); // ぐるぐるの一つ一つで printGirl を実行する
// Cathy is lovely.
// Anna is lovely.
// Sarah is lovely.
// Lucy is lovely.
forEach の引数に printGirl 関数を渡しました。
配列は 「順番が大切なとき」 に使います。
ウェブサイトであれば、例えば "商品リストの並び順" だとか "履歴の表示" だとか。ゲームであれば "順路の探索" だとか "待ち行列の管理" だとか。想像するより遥かに頻繁にコードに登場します。

オブジェクトの魔法

さて次は 「オブジェクト」 です。
オブジェクトは 「順序は関係ない」 ものに使われます。くだけて言えば "何かの詳細を保存しておくもの" です。これにも様々な魔法を実現するための色々な仕掛けがあります。
const age = {
  cathy: 18,
  anna: 33,
  sarah: 4,
  lucy: 82
};

console.log(age.anna); // --> 33
console.log(age['anna']); // --> 33
オブジェクトの 「要素」 の一つ一つは key (キー) とその value (値) で構成されます。
key を指定すると、オブジェクトの中でそれに対応する value が取り出せます。
例えば annakey であれば、その value33 です。
"取り出し方" として上の例では二つの方法を紹介しています。 age.anna としても age['anna'] としても、やっていることは同じです。
またはこんなふうに key を可変な変数にすることも可能です。
let key = 'anna';
console.log(age[key]); // --> 33

key = 'sarah';
console.log(age[key]); // --> 4
実際のプログラミングでは、むしろこっちの方がよく使われます。
value (値) には何を入れても許されます。例えばオブジェクトの中を "入れ子構造" にし、別のオブジェクトを入れても怒られません。
const girl = {
  cathy: { name: 'Cathy', age: 18, spy: false },
  anna: { name: 'Anna', age: 33, spy: false },
  sarah: { name: 'Sarah', age: 4, spy: true },
  lucy: { name: 'Lucy', age: 82, spy: false }
};

const spyFinder = (key) => {
  if (girl[key].spy === true) {
    console.log(`${girl[key].name} is a spy.`);
  } else {
    console.log(`${girl[key].name} is not a spy.`);
  }
};

spyFinder('sarah'); // --> Sarah is a spy.
何でも入れられる!
オブジェクトって最強ですね!
じゃあ今度はこれをちょっと見てみましょう。
const xonsole = {
  log: (s) => console.log(s),
  warn: (s) => console.warn(s),
  error: (s) => console.error(s)
};

xonsole.log('boo!'); // --> boo!
なんと!オブジェクトには value として "関数" を入れることもできるのです!
でもこの xonsole.log ですが
なんか見覚えがあると思いませんか?
xonsolelog という二つの語を . で繋げています。
これまで . でつなげる表現がたくさん出て来ました。
console.log とか navigator.onLine とか document.querySelector とか。
これらの関数たちは "データの形" としては実はこんな構造になっていたのです!
厳密には違うのですが "多かれ少なかれ" です、、、
デフォルトで提供される機能や、外部モジュールを、これから頻繁に使っていきますが、すべてがこのように オブジェクトの中に入った関数 という構造になっているのです。
次の章で 「スコープの話」 を通して、ここらへんを掘り下げて行きましょう。