gorogoronyan FC2

JavaScript: コールバック処理

概略

JavaScript ではコールバック処理を多用します。 すぐにいろんなところで出てくる使い方です。 例えば下のような書き方をします。

  1. 関数の引数で function(){...} が現れる。
    func1( function(param){
        //コールバック処理
    });
    
  2. アロー関数で記述されたコールバック。
    => のような記号が出てきます。
    func1( param => コールバック処理 );  // => はアロー関数を表します。
    

関数式とコールバック

関数式

関数のような手続き処理も変数で扱うことができます。 関数も文字列や数値と同じように変数に代入することができます。 これを 関数式 と呼びます。

例:
let str = "文字列";                   //変数に文字列を代入
let value = 123;                      //変数に数値を代入
let callback = function(param){...};  //変数に関数を代入 (関数式)

参考: MDN:関数式

変数に関数を代入すると、下のような使い方ができます。

1) 変数名で関数を実行することができます。
// 例:
let callback = function(message){
    alert(message);
};

callback("こんにちは");   // 上の関数を実行し、alert のダイアログを表示する。
2) 変数に代入した関数を、別の関数の引数として渡すことができます。
例:
let callback = function(param) {
    //コールバック処理
};

// 変数 callback を関数 foo の引数として渡す
foo(callback);

参考で C言語では上の 1)、2) の話は関数ポインタへの代入、 関数ポインタで実行といった話で出てきます。 Java では関数ポインタの話がないので 不思議な使い方に見えるかもしれません。

無名関数でコールバック

上のプログラムの記述は 1つの処理にまとめて、 下のように書くこともできます。 変数 callback を使わず、関数の引数に直接 function(){...} を書きます。 関数の名前もないので無名関数と呼びます。 このような書き方は JavaScript でよく出てきます。

例:
foo( function(param) {
    //コールバック処理
});

例: アロー関数で書いた場合
foo( param => コールバック処理 );

  コールバック関数の引数が 1個の場合は param に () は不要です。
  また、コールバック処理が 1行の場合は { } や return は不要です。

コールバック処理の記述例

コールバック処理が必要になる例

テキストデータを読み取って、 条件を満たす行だけ抽出する処理を考えてみます。 データは下のようなテキストだったとします。

#先頭の文字が # の行はコメント
#サンプルデータ
果物、りんご
野菜、玉ネギ
果物、バナナ
魚、さんま
野菜、キャベツ
魚、マグロ

抽出したい行の条件は無数にあり、 例えば下のような条件だったとします。

1) データの行だけ抽出する
2) 果物だけ抽出する
3) 魚だけ抽出する
4) 野菜と魚だけ抽出する
5) ひらがなの名前だけ抽出する
6) 4文字の名前だけ抽出する
など

それぞれの処理プログラムはどうなるか?。 概観は下のようになります。

let text = テキストデータを読む;  
let list = text.split(/\r\n|\n|\r/); //text を行の配列に分割
let result = [];                     //抽出結果を格納する配列
for (let i=0; i<list.length; i++){
    let s = list[i];
    if (抽出条件){
        result.push(s);              //条件を満たす行を result に入れる
    }
}
resultを使って次の処理へ
上のプログラムで、if (抽出条件) の部分だけ内容が変わるプログラムになります。 抽出条件は例えば下のようになります。
1) 先頭の文字が # でない
2) データ行 AND 「果物」の文字を含む
3) データ行 AND 「魚」の文字を含む
4) データ行 AND 「野菜」または「魚」の文字を含む
以下、省略

抽出条件が変わるたびに 上のようなコードを毎回書いていたら面倒なので、 うまく汎用化する方法を考えます。 ここでコールバック処理を使います。

/* 共通の処理を関数化
   isMatch(s) が true を返す行だけ配列に入れて返す。

   isMatch : コールバック関数。
     isMatch(s);  s は行の文字列。戻り値は true/false
*/
function getDataList(isMatch){
    let text = テキストデータを読む;
    let list = text.split(/\r\n|\n|\r/);
    let result = [];
    for (let i=0; i<list.length; i++){
        let s = list[i];
        if (isMatch(s)){       //関数の引数の isMatch をここで使う。
            result.push(s);
        }
    }
    return result;
}

//抽出処理の記述は下だけで済む
let isMatch = function(s){
    return (sが抽出条件を満たすか);
};
let result = getDataList(isMatch);

//簡略化して書くと下のような記述になります。
let result = getDataList( s => (sが抽出条件を満たすか) );

//昔風の書き方 (IE でも動かす場合)
let result = getDataList( function(s){
    return (sが抽出条件を満たすか);
});

毎回、最初のサンプルのようなコードを書くのに比べると簡素になります。

サンプル

配列 Array にも filter(isMatch) 関数があります。
JavaScript: 配列と連想配列

文字列の置換処理の例 (2023/11)

文字列の置換処理 String.replace() でも コールバック処理を多用します。

// 例: HTML テキストの <h2> タグに id="ID(通し番号)" の id を付ける。
let counter = 1;
html = html.replace(/<h2>/g, () => `<h2 id="ID${ counter++ }">` );

関連

inserted by FC2 system