Program Structure

Eloquent JavaScript Chapter 2 の読書ノート。

これまでに見てきた言語要素は自然言語ならば名詞や文の断片に相当する。それらを超えて、意味のある文を表現できるところまで、JavaScript 言語への理解を拡張する。そのようなことを述べている。

なお、C/C++ と Python を十分理解している読者はこの章を流し読みで済ませて良いと思われる。

Expressions and statements

  • 値を生成するコードの断片を と呼ぶ。特に、22"psychoanalysis" のようなリテラル値はすべて式だ。

  • 式が文の断片に相当するとすれば、JavaScript における は完全文に相当する。プログラムとは文のリストだ。

  • 最も単純な文はセミコロンが末尾にある式だ。

  • マシンの内部状態を変化させて、後続の文に影響を与えることもある。このような変化を 副作用 と呼ぶ。

  • JavaScript では文の最後のセミコロンを省略できる場合がある。セミコロンを安全に省略できる場合のルールはやや複雑で、間違いを犯しやすい。セミコロンの有無の微妙な違いについて理解を深めるまでは、省略しないで書くことを勧める。

Bindings

本書では binding という用語が頻出するが、当ノートではそれらを変数に適宜置き換える。

let caught = 5 * 5;
let one = 1, two = 2;
var name = "Ayda";
const greeting = "Hello ";
  • プログラム中で値を捕捉して保持するために、JavaScript はバインディング、つまり変数と呼ばれるものを用意している。

  • キーワード let は、この文が変数を定義することを示す。

  • いったん変数が定義されると、その名前を式として使うことができる。

  • 既存の変数に対していつでも演算子 = を使えば、現在の値から切り離して新しい値を指すようにすることができる。

  • 空の変数に値を求めると undefined という値が返ってくる。

  • 単一の let 文で変数を複数定義することができる。そのような定義は , で区切らなければならない。

  • キーワード varconstlet と同じような方法で変数を定義する。

    • var は 2015 年以前の JavaScript で変数が宣言されていた方法だ。紛らわしい特性を持っているため、本書ではほとんど使用しない。

    • const は C++ のそれと同様の意味があるものの、まったく同じではない。

Binding names

  • 変数には、ドル記号 $ やアンダースコア _ を含めることができるが、その他の句読点や特殊文字は使用できない。

  • 他のプログラミング言語と同様に、キーワードや予約語と同一の文字列を変数名とすることは許されない。

The environment

ある時点で存在する変数とその値の集まりを 環境 と呼ぶ。

プログラムが起動したとき、この環境は空ではない。環境には、言語標準の変数が必ず含まれており、ほとんどの場合、周辺のシステムとのやりとりを行うための変数も含まれる。例えば、ブラウザーならば、現在読み込んでいる Web ページと対話的操作をする機能や、マウスやキーボードの入力を読み取る機能がある。

Functions

関数の概念は他のプログラミング言語と同じだ。関数も値の一種と考えることに注意。

  • 関数 prompt は、最近のウェブプログラミングではあまり使われていない。

The console.log function

最新のウェブブラウザーや Node.js を含むほとんどの JavaScript システムには関数 console.log が備わっている。この関数は与えられた引数を何らかのテキスト出力デバイスに書き出す。例えばブラウザーならば、その JavaScript コンソールに出力する。

  • 先ほど句読点は変数名として不適格だと述べたが console.log はそれに該当しない。これは「変数 console が保持する値からプロパティー log を取得する」という式だからだ。

Return values

  • 多くの関数が便利なのは、その副作用があるからだ。

  • 関数は値を生成することもあるが、その場合は副作用がなくても役に立つ。

この見解はプログラミング言語に依存するだろう。

Control flow

  • プログラムに複数の文が含まれている場合、それらはあたかも一つの物語のように上から下へと実行される。

  • 関数 Number は値を数値に変換する。似たような関数に StringBoolean があり、これらの型に値を変換する。

Conditional execution

他のプログラミング同様、JavaScript でもキーワード if により条件付き実行を定義する。構文も同じだ。

let num = Number(prompt("Pick a number"));
if (num < 10) {
    console.log("Small");
} else if (num < 100) {
    console.log("Medium");
} else {
    console.log("Large");
}
  • 関数 Number.isNaN は JavaScript 標準関数であって、与えられた引数が NaN である場合かつそのときに限り true を返す。

  • 関数 Number は有効な数値を表さない文字列を与えると NaN を返す。

while and do loops

この二つのループも他のプログラミング言語のそれらと同様の仕様だ。

let number = 0;
while (number <= 12) {
    console.log(number);
    number = number + 2;
}

let yourName;
do {
    yourName = prompt("Who are you?");
} while (!yourName);
console.log(yourName);
  • プログラムを書くということは、何かをより少なくすることであって、より多くすることではない。

Indenting Code

ソースコードにおけるインデントの意味は C/C++ のそれと同じようだ。処理系から見ると単なる空白でしかない。

  • ほとんどのコードエディタープログラムは、新しい行を自動的に適切な量にインデントしてくれる。

for loops

JavaScript や 類似の言語では、前述のループ構文よりも少し短くてより包括的な形式である for ループを提供している。

for (let number = 0; number <= 12; number = number + 2) {
    console.log(number);
}

Breaking Out of a Loop

JavaScript では C/C++ と同様の break 文と continue 文がサポートされている。

for (let current = 20; ; current = current + 1) {
    if (current % 7 == 0) {
        console.log(current);
        break;
    }
}

Updating bindings succinctly

C/C++ と同様に次の演算子も存在する:

  • +=

  • -=

  • ++

  • --

Dispatching on a value with switch

C や Java から継承した switch 文もサポートするが、そのぎこちなさもそっくり受け継いでいる。

switch (prompt("What is the weather like?")) {
case "rainy":
    console.log("Remember to bring an umbrella.");
    break;
case "sunny":
    console.log("Dress lightly.");
case "cloudy":
    console.log("Go outside.");
    break;
default:
    console.log("Unknown weather type!");
    break;
}

Capitalization

  • 標準の JavaScript 関数、そして JavaScript プログラマーのほとんどが、変数の命名を「最初の単語を除くすべての単語を大文字にする」という様式を採用している。

  • 関数がコンストラクターであるならば、その名前は一文字目から大文字とする。

Comments

C/C++ と同様の方法で、JavaScript コード中にコメントを埋め込むことができる。

Summary

ここまでのノートで代える。

Exercises

  • 演習問題の解答をどのように検証したらよいかわからない場合は、Introduction を参照すること。

  • 各問題は、その説明から始まる。この説明を読んでから演習問題を解くこと。解法がわからない場合は巻末のヒントを参照すること。問題の完全な解答はこの本には含まれていないが <https://eloquentjavascript.net/code> で見ることができる。

  • 問題から何かを学びたい場合には、それを解いた後に、あるいは、最低でもその問題を長時間、必死に取り組んだ後に解答を見ることを勧める。

Looping a triangle

問題console.log を 7 回呼び出して次の三角形を出力するループを書け:

#
##
###
####
#####
######
#######

解答:私の解答を次に記す。ちなみに本書に解答が付いているのかどうかは知らない。以下の演習問題もすべて勝手に解くことにする。

for(let i = 1; i < 8; ++i){
    console.log('#'.repeat(i));
}

FizzBuzz

問題console.log を使って、1 から 100までのすべての数を表示するプログラムを書け。ただし、

  • 3で割り切れる数には、数の代わりに “Fizz” と表示する。

  • また、3 ではなく 5 で割り切れる数は、”Buzz” と表示する。

これができたら、上のただし以降の挙動を維持したままで、3 と 5 の両方で割り切れる数字を“FizzBuzz” と表示するようにプログラムを変更しろ。

解答:この問題はしばしば目にするのだが、何が面白いのかわからない。前半は:

for(let i = 1; i < 101; ++i){
    if(i % 3 == 0){
        console.log("Fizz");
    }
    else if(i % 5 == 0){
        console.log("Buzz");
    }
    else{
        console.log(i);
    }
}

後半はこれを改造するわけだが、最初の if ブロックだけを修正すれば十分だ。

for(let i = 1; i < 101; ++i){
    if(i % 3 == 0){
        if(i % 5 == 0){
            console.log("FizzBuzz");
        }
        else{
            console.log("Fizz");
        }
    }
    else if(i % 5 == 0){
        console.log("Buzz");
    }
    else{
        console.log(i);
    }
}

i % 3 == 0i % 5 == 0 は各反復ごとにただ一度ずつ必ず計算するので、 divisible_by_3 のような一時変数を設けてもいいかもしれない。

Chessboard

問題:8×8 のグリッドを表す文字列を、改行文字を使って行を区切って作成するプログラムを書け。グリッドの各位置には、スペースまたは記号 # があり、チェス盤のようになる。

このパターンを生成するプログラムができたら、変数 size = 8 を定義して、任意のサイズで動作するようにプログラムを変更し、与えられた幅と高さのグリッドを出力しろ。

解答:いきなり後半から取り組んでもいいだろう:

const size = 8;
for(let i = 0; i < size; ++i){
    if(i % 2 == 0){
        console.log(' '.padEnd(size, '# '));
    }
    else{
        console.log('#'.padEnd(size, ' #'));
    }
}

以上