つくる 処理を関数に整理する
このレッスンで分かること
- 1 本の大きな関数を、役割ごとの小さな関数に分けられます
- ガード節、デフォルト引数、
forEachを 1 つのコードで組み合わせます
検査も組み立ても 1 本に詰め込むと、直す場所が決まらない
第 6 章では、関数まわりの道具を 7 つ手に入れました。名前を付ける関数宣言、短く書くアロー関数、省略に備えるデフォルト引数、入口ではじくガード節、変数を閉じ込めるスコープ、関数を渡すコールバック、そして配列をまわす forEach です。今回はその総仕上げとして、プロフィールページに出す文字列を組み立てる buildProfilePage を書きます。作りたいのは次の形です。
プレーンテキスト
こんにちは、山田 太郎さん
Web エンジニアを目指しています
1. プロフィールサイト
2. 自己紹介カードこれを 1 本の関数に全部書くこともできます。ですがそうすると、挨拶の文言を変えたいときも、作品の並べ方を変えたいときも、入力の検査を足したいときも、同じ 1 本を開くことになります。どこを直せばいいのか分からない、という状態はここから生まれます。
仕事の性質で分ける
仕様を読み直すと、性質の違う仕事が 3 つ混ざっています。
| 仕事 | 担当 |
|---|---|
| 挨拶の 1 行を作る | 挨拶担当の関数 |
| 作品の一覧を組み立てる | 一覧担当の関数 |
| 入力を検査して全体をつなぐ | buildProfilePage |
分ける利点は、直す場所が 1 か所に決まることです。挨拶を変えたければ挨拶担当だけを見ればよく、他は開かなくて済みます。まとめ役は、部品を呼んでつなぐことに集中します。
形だけ、別の題材で見ておきます。
JavaScript
function buildCard(title, body) {
if (typeof title !== "string" || title === "") return "表示できません";
return cardHeader(title) + "\n" + body;
}最初の行が「受け付けない入力」、最後の行が「やりたいこと」。本体が 1 行で、しかも一番浅いインデントにあります。この形にたどり着けるのが第 6 章の成果です。検査はまとめ役の 1 か所に集め、部品の側では書きません。部品は正常な値が来る前提で書いてよい、と約束しておくと全体が読みやすくなります。
分けすぎにも気を付けてください。1 行しかない処理を関数にすると、定義を探しに行く手間のほうが大きくなります。同じ処理が 2 か所以上に出てくる、名前が付けられて数行ある、独立して確かめたい。このどれかに当てはまるときだけ分けます。
配列かどうかは typeof では分からない
配列が来るはずのところへ null が届く、という場面はよくあります。ところが typeof は、配列も null も "object" と答えるので判定に使えません。
JavaScript
console.log(typeof ["A", "B"]); // object
console.log(typeof null); // object
console.log(Array.isArray(["A", "B"])); // true
console.log(Array.isArray(null)); // false配列かどうかを確かめたいときは Array.isArray を使います。ガード節に並べておけば、本体では中身があることだけを考えればよくなります。
よくある間違い
- 作業用の配列を関数の外に置く — 行を集める配列を外に置くと、2 回目の呼び出しに前回の行が残ります。スコープのレッスンで見たとおり、宣言は関数の中です
- 空配列のときを決めていない — 空の配列をつないでも空文字列になるだけで、エラーは出ません。画面には不自然な余白だけが残ります。何を返すかを先に決めてください
要件
- 関数名は buildProfilePage、引数は name, works, role の順にすること
- role にはデフォルト引数
Web エンジニアを目指していますを設定すること - 不正な入力はガード節で冒頭にはじき、文字列
入力エラーを返すこと - 挨拶を作る関数と作品一覧を作る関数に分け、buildProfilePage から呼び出すこと
入出力例
buildProfilePage("山田 太郎", ["プロフィールサイト","自己紹介カード","スキル一覧の表"]) → "こんにちは、山田 太郎さん
Web エンジニアを目指しています
1. プロフィールサイト
2. 自己紹介カード
3. スキル一覧の表"
buildProfilePage("鈴木 花子", ["自己紹介カード"], "デザイナー") → "こんにちは、鈴木 花子さん
デザイナー
1. 自己紹介カード"
buildProfilePage("山田 太郎", []) → "こんにちは、山田 太郎さん
Web エンジニアを目指しています
作品はまだありません"
buildProfilePage("", ["プロフィールサイト"]) → "入力エラー"
buildProfilePage("山田 太郎", null) → "入力エラー"