JavaScriptの関数・スコープ・モジュール:処理を再利用する方法
JavaScriptの関数、引数と戻り値、アロー関数、クロージャー、exportとimportを実例で解説。平均値の計算と独立したカウンターを作ります。
第2回では、引数から結果を返す関数、変数が使える範囲であるスコープ、関数が外側の値を保持するクロージャーを学びます。最後にファイルを分割し、exportとimportで処理を再利用します。前提は基本構文の記事です。
関数の入力と出力を決める
returnで計算結果を返す
関数は引数を受け取り、returnで呼び出し元へ値を返します。console.logは表示する処理であり、戻り値を返す処理とは別です。入力の検証、計算、表示を分けると、同じ計算を別の画面からも使いやすくなります。
stats.mjsを作成する
作業用フォルダーにstats.mjsを作成します。配列が空の場合や、有限な数値以外が入っている場合を拒否し、平均値を計算します。
export function average(values) {
if (!Array.isArray(values) || values.length === 0) {
throw new TypeError('空でない配列を渡してください。');
}
let total = 0;
for (const value of values) {
if (!Number.isFinite(value)) {
throw new TypeError('すべての要素を有限な数値にしてください。');
}
total += value;
}
return total / values.length;
}
export const formatAverage = (value) => `平均:${value.toFixed(1)}`;
export function createCounter(initialValue = 0) {
let count = initialValue;
return () => {
count += 1;
return count;
};
}
スコープとクロージャーを理解する
関数の中の変数を外から直接使わない
totalはaverageの中で宣言され、その関数内の計算に使います。処理の途中の値をグローバル変数へ置かず、必要な結果を戻り値として公開します。
letとconstにはブロックスコープがあります。条件分岐やループのブロック内で宣言した変数は、通常その外から参照できません。
クロージャーで別々の状態を保持する
createCounterが返す関数は、外側のcountを参照し続けます。これがクロージャーの例です。呼び出すたびに別のcountが作られるため、2つのカウンターを独立させられます。
この例では初期値に数値を渡す前提です。外部入力を受け付ける部品にするなら、Number.isSafeIntegerなどによる範囲と型の検証も追加してください。
importして利用する
main.mjsを作成する
stats.mjsと同じフォルダーにmain.mjsを作成します。相対パスには拡張子を含めます。
import { average, formatAverage, createCounter } from './stats.mjs';
console.log(formatAverage(average([80, 65, 92]))); // 平均:79.0
console.log(formatAverage(average([100, 80]))); // 平均:90.0
const first = createCounter();
const second = createCounter(10);
console.log(first()); // 1
console.log(first()); // 2
console.log(second()); // 11
try {
average([]);
} catch (error) {
console.log(error.message);
}
モジュールを実行する
Node.jsで次のコマンドを実行します。stats.mjsには表示処理を置いていないので、importしただけで勝手に例が動くことはありません。
node main.mjs
アロー関数と通常の関数
短い変換にアロー関数を使う
formatAverageのような短い式はアロー関数で書けます。波括弧を使う関数本体では、結果を返すために明示的なreturnが必要です。省略できるのは式をそのまま本体にした場合です。
thisの扱いは同じではない
アロー関数には独自のthisがなく、外側のthisを使います。通常の関数のthisは呼び出し方によって変わります。メソッドとコールバックの違いは、後のクラス入門で扱います。
練習と関連資料
入力の境界を試す
average([0])の結果を確認します。average([80, '90'])が文字列を拒否することを確認します。createCounter()を3つ作り、互いの値が混ざらないことを確認します。
次に配列とオブジェクトを学ぶ
次は配列とオブジェクトの実践で、商品の絞り込み・変換・集計を実装します。