JavaScript のコールバック関数の理解と使用

JavaScript のコールバック関数の理解と使用

概要

JavaScript では、関数はファーストクラス オブジェクトです。つまり、関数はファーストクラス オブジェクトのように管理および使用できます。関数は実際にはオブジェクトであるため、変数に「保存」したり、関数パラメータとして渡したり、関数内で作成したり、関数から返したりすることができます。

関数はファーストクラスオブジェクトなので、JavaScript ではコールバック関数を使用できます。次の記事では、コールバック関数についてすべて学習します。コールバック関数は、おそらく JavaScript で最もよく使用される関数型プログラミング手法です。一見、JavaScript または jQuery コードのほんの一部にしか見えませんが、多くの開発者にとって謎のままです。この記事を読めば、コールバック関数の使い方が理解できるようになります。

コールバック関数は、関数型プログラミングと呼ばれるプログラミング パラダイムから派生した概念です。簡単に言えば、関数型プログラミングでは関数を変数として使用します。関数型プログラミングは、当時は(そして現在でも広くは使用されていませんが)、特別に訓練された熟練プログラマーの秘密のスキルと見なされることが多かったのです。

幸いなことに、関数は、あなたや私のような一般の人でも簡単に使用できるほど十分に説明されているプログラミング スキルです。関数型プログラミングの主なテクニックの 1 つはコールバック関数です。次の内容では、コールバック関数の実装が、通常の関数にパラメータを渡すのと同じくらい簡単であることがわかります。このテクニックは非常に単純なので、なぜ高度な JavaScript テクニックに関する章によく含まれているのか不思議に思うことがよくあります。

コールバックまたは高階関数とは何ですか?

コールバック関数は、高階関数とも呼ばれ、コールバック関数が呼び出される別の関数 (ここでは他の関数を otherFunction と呼びます) に引数として渡される関数です。コールバック関数は本質的にプログラミング パターン (一般的な問題に対して作成されたソリューション) であるため、コールバック関数を使用することはコールバック パターンとも呼ばれます。

以下は、jQuery でコールバック関数を使用する簡単で一般的な例です。

//クリックメソッドは変数ではなく関数であることに注意してください。 //これはコールバック関数です $("#btn_1").click(function() {
    alert("ボタン1がクリックされました");
});

前の例でわかるように、関数をパラメータとしてクリック メソッドに渡しました。 click メソッドは、渡された関数を呼び出し (または実行) します。これは JavaScript におけるコールバック関数の典型的な使用法であり、jQuery でも広く使用されています。

以下は、JavaScript の一般的なコールバック関数の別の例です。

var friends = ["マイク", "ステイシー", "アンディ", "リック"];

friends.forEach(関数 (eachName, インデックス){
    console.log(index + 1 + ". " + eachName); // 1. Mike、2. Stacy、3. Andy、4. Rick
});

ここでも、匿名関数 (名前のない関数) を forEach メソッドのパラメーターとして渡していることに注意してください。

これまで、匿名関数を別の関数またはメソッドへの引数として渡してきました。より実用的な例を見て独自のコールバック関数を記述する前に、まずコールバック関数がどのように機能するかを理解しましょう。

コールバック関数はどのように機能しますか?

関数は JavaScript のファーストクラス オブジェクトであるため、関数をオブジェクトのように扱い、関数を変数のように渡したり、関数から関数を返したり、他の関数から関数を使用したりすることができます。コールバック関数を別の関数の引数として渡す場合、関数定義のみが渡されます。引数に対して関数を実行していません。通常、関数を実行するときに行うように、関数を括弧 () のペアで渡すことはありません。

コールバック関数はすぐに実行されないことに注意することが重要です。名前が示すように、これはそれを含む関数内の特定の時点でコールバックされます。したがって、最初の jQuery の例は次のようになります。

//匿名関数はパラメータ内では実行されません //これはコールバック関数です $("#btn_1").click(function(){
    alert("ボタン1がクリックされました");
});

この匿名関数は、後で関数本体内で呼び出されます。名前が付いていても、それを含む関数内の引数オブ​​ジェクトを通じてアクセスできます。

コールバック関数はクロージャである

コールバック関数を変数として別の関数に渡すと、コールバック関数は、その関数内でコールバック関数が定義されている場合と同様に、その関数内の特定の時点で実行されます。つまり、コールバック関数は本質的にクロージャです。

ご存知のように、クロージャはそれを含む関数のスコープに入ることができるため、コールバック関数は、それを含む関数内の変数だけでなく、グローバル スコープ内の変数にもアクセスできます。

コールバック関数の実装の基本原則

コールバック関数は複雑ではありませんが、コールバック関数の作成と使用を開始する前に、コールバック関数の実装に関するいくつかの基本原則を理解しておく必要があります。

名前付き関数または匿名関数をコールバックとして使用する

前の jQuery の例と forEach の例では、パラメーターの位置に定義された匿名関数をコールバック関数として使用しました。これはコールバック関数で使用される一般的なマジックトリックです。もう 1 つの一般的なパターンは、名前付き関数を定義し、関数名を変数として関数に渡すことです。たとえば、次の例をご覧ください。

//グローバル変数 var allUserData = [];

//通常のlogStuff関数。内容をコンソールに出力します。function logStuff (userData) {
    if ( typeof userData === "文字列"){
        コンソールにユーザーデータをログ出力します。
    } そうでない場合 (typeof userData === "object"){
        for(var item in userData){
            console.log(item + ": " + userData[item]);
        }
    }
}

// 2つのパラメータを受け取る関数。後者はコールバック関数です。 function getInput (options, callback) {
    allUserData.push(オプション);
    コールバック(オプション);
}

// getInput 関数を呼び出すときに、logStuff をパラメータとして渡します // そのため、logStuff は getInput 関数内でコールバック (または実行) されます
getInput({name:"Rich",speciality:"Javascript"}, logStuff);
//名前:リッチ
// 専門分野:Javascript

コールバック関数にパラメータを渡す

コールバック関数は実行されると通常の関数となるため、パラメータを渡すことが可能です。包含関数 (またはグローバル プロパティ) の任意のプロパティを、コールバック関数への引数として渡すことができます。前の例では、オプションをパラメーターとしてコールバック関数に渡しました。ここで、グローバル変数とローカル変数を渡します。

//グローバル変数 var generalLastName = "Cliton";

関数 getInput (オプション、コールバック) {
    allUserData.push (オプション);
    // グローバル変数 generalLastName をコールバック関数 callback(generalLastName,options); に渡します。
}

コールバックを実行する前に関数であることを確認してください

引数として渡されたコールバックを呼び出す前に、それが実際に関数であるかどうかを確認するのが賢明です。また、これは条件付きコールバックを実装するのに最適なタイミングです。

上記の例の getInput 関数をリファクタリングして、チェックが適切であることを確認しましょう。

関数 getInput(オプション, コールバック){
    allUserData.push(オプション);
    
    // コールバックが関数であることを確認する if (typeof callback === "function") {
        //呼び出し可能になったので、これを呼び出します。callback(options);
    }
}

適切なチェックを行わないと、getInput に引数としてコールバック関数がない場合、または渡されたコールバック関数が実際には関数ではない場合に、コードでランタイム エラーが発生します。

このオブジェクトのメソッドをコールバック関数として使用する場合の問題

コールバック関数が this オブジェクトのメソッドである場合、 this オブジェクトのコンテキストを確保するために、コールバック関数の実行方法を変更する必要があります。それ以外の場合、コールバックがグローバル関数に渡されると、this オブジェクトはグローバル ウィンドウ オブジェクト (ブラウザー内) を参照します。どちらもメソッドを含むオブジェクトを指します。

これを次のコードで説明します。

//いくつかのプロパティとメソッドを持つオブジェクトを定義します //次に、メソッドを別の関数へのコールバックとして渡します var clientData = {
    id: 094545,
    fullName "未設定"、
    //setUsrName は clientData オブジェクトのメソッドです setUserName: function (firstName, lastName) {
        //これはオブジェクトの fullName プロパティを参照します。this.fullName = firstName + " " + lastName;
    }
} 

関数 getUserInput(firstName, lastName, コールバック){
    //firstName/lastNameを確認するためにここで何かする
    //名前を保存します
    コールバック(firstName, lastName);
}

以下のコード例では、clientData.setUsername が実行されても、this.fullName は clientData オブジェクトの fullName プロパティを設定しません。代わりに、getUserInput はグローバル関数であるため、window オブジェクトの fullName プロパティが設定されます。これは、グローバル関数内の this オブジェクトが window オブジェクトを指しているためです。

getUserInput("バラク","オバマ",clientData.setUserName);
console.log(clientData,fullName); // 未設定
//fullName プロパティはウィンドウ オブジェクトで初期化されます console.log(window.fullName); //バラク オバマ

保存するには、Call関数とApply関数を使用します。

上記の問題を解決するには、Call または Apply 関数を使用できます。これまでのところ、JavaScript のすべての関数には Call と Apply という 2 つのメソッドがあることがわかりました。これらのメソッドは、関数内で this オブジェクトを設定し、関数に変数を渡すために使用されます。

call によって受け取られる最初の引数は、関数内で this として使用されるオブジェクトであり、関数に渡される引数は 1 つずつ渡されます (もちろん、カンマで区切られます)。 Apply 関数の最初の引数は関数内で this として使用されるオブジェクトでもありますが、最後の引数は関数に渡される値の配列です。

複雑に聞こえますが、Apply と Call を使用してどれほど簡単にできるか見てみましょう。前の例の問題を修正するには、以下の例で Apply 関数を使用します。

//「callbackObj」というコールバック オブジェクトとして新しいパラメータを追加したことに注意してください。
関数 getUserInput(firstName, lastName, callback.callbackObj){
    //ここで名前を確認するための操作を行います callback.apply(callbackObj, [firstName, lastName]);
}

Apply 関数で this オブジェクトを正しく設定すると、コールバックが正しく実行され、clientData オブジェクトの fullName プロパティが正しく設定されます。

// clientData.setUserName メソッドと clientData オブジェクトをパラメーターとして使用します。clientData オブジェクトは、Apply メソッドによってこのオブジェクトを設定するために使用されます。getUserName("Barack", "Obama", clientData.setUserName, clientData);

//clientData の fullName プロパティが正しく設定されています console.log(clientUser.fullName); //バラク・オバマ

Call 関数を使用することもできますが、この例では Apply 関数を使用します。

複数のコールバック関数を許可する

複数の変数を渡すことができるのと同様に、複数のコールバック関数を関数の引数として渡すことができます。以下は jQuery での AJAX の例です。

関数 successCallback(){
    //送信前に何かする }
  
関数 successCallback(){
//メッセージが正常に受信された後に何かを実行する}

関数completeCallback(){
//完了後に何かを行う}

関数 errorCallback(){
    //エラーが発生したときに何かを行う}

$.ajax({
    url:"upload/2022/web/favicon.png",
    成功:successCallback、
    完了:completeCallback、
    エラー:エラーコールバック
});

「コールバック地獄」問題とその解決法

非同期コードを実行する場合、単純にどのような順番でコードを実行しても、コールバック関数が何段にも積み重なって以下のような状況になってしまうことが多々あります。このコードの混乱は、コールバックが多すぎてコードを理解するのが非常に困難になるため、コールバック地獄と呼ばれます。私は、Node.js 用の MongoDB ドライバーである node-mongodb-native から例を取り上げました。次のコードはコールバック地獄を完全に示しています。

var p_client = 新しい Db('integration_tests_20', 新しい Server("127.0.0.1", 27017, {}), 
                 {'pk':カスタムPKファクトリー});
p_client.open(関数(err, p_client) {
    p_client.dropDatabase(function(err, done) {
        p_client.createCollection('test_custom_key', 関数(err, コレクション) {
            コレクション.insert({'a':1}, 関数(err, docs) {
                コレクション.find({'_id':新しいオブジェクトID("aaaaaaaaaaaaa")}, 
                関数(エラー、カーソル) {
                    カーソル.toArray(関数(err, 項目) {
                        test.assertEquals(1, 項目の長さ);
                        // データベースを閉じましょう
                        p_client.close();
                    });
                });
            });
        });
    });
});

コード内でこのような問題に遭遇することは望ましくありませんが、もしこの問題に遭遇した場合、

  • このような状況に時々遭遇するでしょう
  • この問題には 2 つの解決策があります。
  • 匿名関数をメイン関数の引数として定義するのではなく、関数に名前を付けてコールバックとして渡します。
  • モジュール化により、コードをモジュールに分割して、特定のジョブを実行するコードの一部を切り分けることができます。その後、メガアプリにモジュールをインポートできます。

独自のコールバック関数を作成する

これで、JavaScript のコールバックについて知っておくべきことはすべて完全に理解できたと思います (さっきもう一度読んでいなければ、理解できたと思います)。また、コールバックの使用がいかにシンプルで強力であるかもわかりました。次は、コードを調べて、コールバックを使用できる場所があるかどうかを確認してください。コールバック関数は、次の点で役立ちます。

  • コードの重複を避ける (DRY - Don't Repeat Yourself)
  • より優れた抽象化により、多目的な機能が増えます(すべての機能はそのままです)
  • コードの保守性を高める
  • コードを読みやすくする
  • より専門的な関数を書く

コールバック関数の作成は非常に簡単です。次の例では、ユーザーの情報を読み取り、そのデータを使用して一般的な詩を作成し、ユーザーを歓迎する関数を作成します。これは、多くの if/else ステートメントが含まれ、ユーザー データに必要な関数を呼び出す際に多くの制限と非互換性があるため、非常に複雑な関数になります。

代わりに、コールバック関数を使用して追加機能を実装しました。これにより、ユーザー情報を取得するメイン関数は、ユーザーのフルネームと性別をパラメーターとしてコールバック関数に渡して実行するだけで、必要な操作を実行できるようになります。

つまり、getUserInput 関数は多機能であり、さまざまな機能を持つコールバック関数を実行できます。

//まず、汎用詩生成関数を作成します。これは、以下のgetUserInput関数のコールバック関数として使用されます。function genericPoemMaker(name, gender) {
    console.log(name + " は上質なワインよりも素晴らしいです。");
    console.log("現代において利他的で高貴な行為。");
    console.log("常に最新のスタイルで見事に装飾されています。");
    console.log("不幸な悲劇の連続だが、それでも常に笑顔を絶やさない");
}

//コールバック、最後のパラメータは上で定義した genericPoemMaker 関数になります function getUserInput(firstName, lastName, gender, callback) {
    var fullName = firstName + " " + lastName;
    // コールバックが関数であることを確認する
    if (typeof コールバック === "function") {
    // コールバック関数を実行し、パラメータを渡す
    コールバック(fullName, 性別);
    }
}

getUserInput 関数を呼び出し、genericPoemMaker 関数をコールバック関数として使用します。

getUserInput("マイケル", "ファスベンダー", "男性", genericPoemMaker);
// 出力 /* マイケル・ファスベンダーは上質なワインよりも素晴らしい。
現代にふさわしい利他的で高貴な作品。
常に最新のスタイルで見事に装飾されています。
不幸な悲劇を経験しながらも、常に笑顔を絶やさない男。
*/

getUserInput 関数はデータの抽出のみを担当するため、任意のコールバック関数を渡すことができます。たとえば、greetUser 関数を渡すことができます。

関数greetUser(顧客名, 性別) {
    var salutation = sex && sex === "Man" ? "Mr." : "Ms.";
    console.log("こんにちは、" + 挨拶 + " " + 顧客名);
}

// コールバック関数としてgreetUserを使用します。getUserInput("Bill", "Gates", "Man", greetingUser);

// これは出力です こんにちは、ビル・ゲイツさん

まったく同じ getUserInput 関数を呼び出しますが、今回はまったく異なるタスクを実行します。

ご覧のとおり、コールバック関数は魔法のようなものです。前の例は比較的単純でしたが、destroy 関数を使い始めるだけで、どれだけの作業が節約でき、コードがどれだけ抽象化されるか想像してみてください。大胆に使ってください。

コールバックは、JavaScript プログラミング、特に最新の Web アプリケーション開発、ライブラリ、フレームワークにおいて、さまざまな方法でよく使用されます。

  • 非同期呼び出し (ファイルの読み取り、HTTP リクエストの作成など)
  • イベントリスナー/ハンドラー
  • setTimeout および setInterval メソッド
  • 一般: コードを合理化する

結論

JavaScript コールバック関数は素晴らしく強力であり、Web アプリケーションとコードに多くの利点をもたらします。必要に応じて使用するか、抽象化、保守性、可読性のためにコールバックを使用するようにコードをリファクタリングする必要があります。

以上が、JavaScript のコールバック関数の理解と使用の詳細です。JavaScript の詳細については、123WORDPRESS.COM の他の関連記事にも注目してください。

以下もご興味があるかもしれません:
  • js コールバック関数の原理と使用例の分析
  • JavaScript コールバック関数の使用状況分析
  • JSコールバック関数の詳細な理解
  • JSコールバック関数のわかりやすい例分析
  • ネイティブJSでjsファイルを動的に読み込み、読み込み成功後にコールバック関数を実行する方法
  • コールバック関数とその JS での使用法の簡単な分析
  • JavaScript コールバック関数の使用例の分析
  • JavaScriptコールバック関数の詳細な説明
  • JavaScript コールバック関数の概念的理解と使用法の分析
  • JSコールバック関数の基本的な定義と使用例の分析

<<:  EF (Entity Framework) の挿入または更新データ エラーの解決方法

>>:  TomcatをダウンロードしてLinuxにインストールする詳細な手順

推薦する

JSはタイムラインの自動再生を実現する

最近、次のような効果を実装しました。再生ボタンをクリックするとタイムラインの再生が開始され、一時停止...

ドロップダウンリストのJavaScript実装

この記事の例では、ドロップダウンリストを実装するためのJavaScriptの具体的なコードを参考まで...

CSSフローティングとフローティング解除について

フロートの定義要素を通常のドキュメント フローから外し、要素を左また​​は右に近づけます。親要素の端...

垂直グリッドと漸進的な行間隔の例

新しい質問急いで来て、急いで行ってください。 「垂直グリッドとプログレッシブ行間隔 (パート 1)」...

Linux SSHポートを転送する3つの方法

ssh は私が最も頻繁に使用する 2 つのコマンドライン ツールのうちの 1 つです (もう 1 つ...

Docker のホスト間コンテナ通信オーバーレイ実装プロセスの詳細な説明

サーバーも 2 つあります。準備:コンテナのホスト名を設定する consul: kv タイプのストレ...

mysqlは指定された期間内の統計データを取得します

mysqlは指定された期間内の統計データを取得します年別統計 選択 カウント(*)、 DATE_FO...

ヘッダーのチェックボックスをテキスト実装コードに変更するための選択テーブルを持つ要素

方法1: テーブル属性を使用する: header-cell-class-name テーブルインターフ...

MySQL 5.7.20\5.7.21 無料インストール版のインストールと設定のチュートリアル

参考までに、mysql 5.7.20 / 5.7.21 をダウンロード、インストール、構成します。具...

Dockerでudpポート番号を指定する問題を解決する

Docker はコンテナを起動するときにアクセス ポートを指定します。複数の -p オプションを使用...

jQueryの競合問題を解決する方法

フロントエンド開発において、$ は jQuery の関数です。$ のパラメータが異なると、実装される...

Nodejs は readline を使用してコンテンツ入力を促すサンプルコード

目次序文1. batがjsを実行する2. ターミナルにバージョン番号を入力してパッケージ化コマンドを...

Reactドラッグフックを実装するための100行以上のコード

序文ソースコードは合計で 100 行強しかありません。これを読めば、react-dnd などの成熟し...

MySQL はどのようにしてデータの整合性を確保するのでしょうか?

オンライン ビジネスにとってデータの一貫性と整合性が重要であることは明らかです。データが失われないよ...

Eclipse は Tomcat を構成しますが、Tomcat には無効なポート解決策があります

目次1. EclipseがTomcatを構成する2. Tomcat の無効なポートの解決方法方法1:...