コールバック関数


01コールバック関数は何ですか。


コールバック関数は、パラメータを他のコード(関数またはメソッド)に渡し、その制御権を同時に委任する関数です.
コールバック関数を許可するコードは、自身の内部ロジックに基づいて適切な時間にこのコールバック関数を実行します.

02制御権


4-2-1コールポイント

var count = 0;
var cbFunc = function () {
  console.log(count);
  if (++count > 4) clearInterval(timer);
};

var timer = setInterval(cbFunc, 300);
コード呼び出しマスター制御権cbFunc()ユーザユーザユーザ設定Interval(cbFunc,300);setIntervalsetIntervalsetIntervalを第1パラメータとしてcbFunc()を超えると、cbfuncの制御権はsetIntervalによって得られ、自分の判断(setIntervalの第2パラメータはミリ秒を超える)に基づいてcbfuncが実行される.
これにより,コールバック関数の制御権を受けるコードは,コールバック関数呼び出し時点の制御権を持つ.

4-2-2因子

var newArr = [10, 20, 30].map(function (currentValue, index) {
  console.log(currentValue, index);
  return currentValue + 5;
});
console.log(newArr);
/**
 * 10 0
 * 20 1
 * 30 2
 * [15, 25, 35]
 */

var newArr2 = [10, 20, 30].map(function (index, currentValue) {
  console.log(index, currentValue);
  return currentValue + 5;
});
console.log(newArr2);
/**
 * 10 0
 * 20 1
 * 30 2
 * [5, 6, 7]
 */
mapメソッド
  • メソッドのターゲット配列のすべての要素を最初から1つずつ取り出し、コールバック関数を繰り返し呼び出し、コールバック関数の実行結果を収集して新しい配列を作成します.
  • コールバック関数の最初のパラメータ:配列内の要素の現在の値
  • の2番目のパラメータ:現在の値のインデックス
  • 第3のパラメータ:マッピング方法のターゲット配列自体
  • mapメソッドを呼び出して必要な配列を取得するには、mapメソッドで定義されたルールに基づいて関数を記述する必要があります.
    (newArr 1とnewArr 2の出力結果を比較するとわかります.)
    mapメソッドで定義されたルールには、コールバック関数パラメータとして使用する値とその順序も含まれます.
    このように、コールバック関数の制御権を受けるコードは、コールバック関数を呼び出すときに、パラメータにどの値をどのような順序で渡すかの制御権を有する.

    4-2-3 this


    コールバック関数(関数によって宣言されたコールバック関数)も関数であるため、デフォルトではグローバルオブジェクトが参照されますが、制御権を受け入れるコードにコールバック関数として使用する別のターゲットが指定されている場合は、そのターゲットが参照されます.
    このターゲットを個別に指定する方法.
  • コール/アプリケーション/バインディング(明示的バインディング)
  • 例)forEachでは、2番目のパラメータを使用して設定できます.
  • を選択します.
    setTimeout(function () {
      console.log(this);
    }, 300); // window
    
    [1, 2, 3, 4, 5].forEach(function (x) {
      console.log(this); // window * 5
    });
    
    element.querySelector("element").addEventListener("click", function (e) {
      console.log(this); // element
    });
    関数宣言文で記述されている場合、このオプションは明示的にバインドされていないウィンドウオブジェクトを指します.
    addEventListenerのcallback関数の場合、例外的にイベントターゲットはここにバインドされます.
  • 矢印関数を使用して記述されている場合は、例外ではありません.△大尉顕微鏡のthisを指す.
  • 矢印関数として使用する場合
    setTimeout(() => console.log(this), 300); // 상위 스코프의 this 즉, window
    
    [1, 2, 3, 4, 5].forEach(() => console.log(this)); // 상위 스코프의 this 즉, window
    
    element
      .querySelector("element")
      .addEventListener("click", () => console.log(this)); // 상위 스코프의 this 즉, window
    矢印関数のthisはバインドされていません.親ミラーのthisを参照します.

    03.コールバック関数は関数


    名前の通り、コールバック関数は関数です.
    コールバック関数を使用してオブジェクトを渡すメソッドでも、メソッドではなく関数として呼び出されます.
    var obj = {
      vals: [1, 2, 3],
      logValues: function (v, i) {
        console.log(this, v, i);
      },
    };
    obj.logValues(1, 2); // {vals: [1, 2, 3], logValues: f} 1 2
    [4, 5, 6].forEach(obj.logValues);
    /**
     * window{} 4 0
     * window{} 5 1
     * window{} 6 2
     */
    オブジェクトのメソッドを関数のパラメータに渡しても、これは最終的にはメソッドではなく関数にすぎません.

    04.コールバック関数でthisに別の値をバインドする


    ES 5のbindメソッドの使用方法

    var obj1 = {
      name: "obj1",
      func: function () {
        console.log(this.name);
      },
    };
    
    setTimeout(obj1.func.bind(obj1), 1000);
    
    var obj2 = { name: "obj2" };
    setTimeout(obj1.func.bind(obj2), 1500);

    05.呼地獄と非同期制御


    コールバック地獄
  • コールバック関数を匿名関数に渡すプロセスを繰り返し、コードのインデント深さに耐えられない
    非同期
  • 非同期コードは、現在実行されているコードが完了しているかどうかにかかわらず、すぐに次のコードにジャンプします.
  • 要求、実行待ち、保留等に関するコードは非同期コード
  • である.
    同期
  • 同期コードは、現在実行中のコードが完了してから、次のコードを実行します.
  • callback地獄の例)

    setTimeout(
      function (name) {
        var coffeeList = name;
        console.log(coffeeList);
    
        setTimeout(
          function (name) {
            coffeeList += `, ${name}`;
            console.log(coffeeList);
    
            setTimeout(
              function (name) {
                coffeeList += `, ${name}`;
                console.log(coffeeList);
    
                setTimeout(
                  function (name) {
                    coffeeList += `, ${name}`;
                    console.log(coffeeList);
                  },
                  500,
                  "카페라떼"
                );
              },
              500,
              "카페모카"
            );
          },
          500,
          "아메리카노"
        );
      },
      500,
      "에스프레소"
    );
    上記の例では、0.5秒ごとにコーヒーリストを収集して出力します.
    目的達成の妨げにはならないが,インデントの程度が深まるだけでなく,伝達値の順序が下から上に上がるのは気まずい.

    Promiseの使用例)

    new Promise(function (resolve) {
      setTimeout(function () {
        var name = "에스프레소";
        console.log(name);
        resolve(name);
      }, 500);
    })
      .then(function (prevName) {
        return new Promise(function (resolve) {
          setTimeout(function () {
            var name = `${prevName}, 아메리카노`;
            console.log(name);
            resolve(name);
          }, 500);
        });
      })
      .then(function (prevName) {
        return new Promise(function (resolve) {
          setTimeout(function () {
            var name = `${prevName}, 카페모카`;
            console.log(name);
            resolve(name);
          }, 500);
        });
      })
      .then(function (prevName) {
        return new Promise(function (resolve) {
          setTimeout(function () {
            var name = `${prevName}, 카페라떼`;
            console.log(name);
            resolve(name);
          }, 500);
        });
      });
    new演算子とともに呼び出されたPromiseパラメータに渡されるコールバック関数は、呼び出されるとすぐに実行されますが、resolve、execute関数の内部に構文がある場合、この2つの関数を実行する前に次の(then)またはエラー構文(catch)に移動しません.したがって、非同期操作が完了した場合にのみ、resolveまたはrejectを呼び出すことで非同期操作の同期表現を実現することができる.

    Promise+モジュール表示方法例)

    var addCoffee = function (name) {
      return function (prevName) {
        return new Promise(function (resolve) {
          setTimeout(function () {
            var newName = prevName ? `${(prevName, name)}` : name;
            console.log(newName);
            resolve(newName);
          }, 500);
        });
      };
    };
    
    addCoffee("에스프레소")()
      .then(addCoffee("아메리카노"))
      .then(addCoffee("카페모카"))
      .then(addCoffee("카페라떼"));

    Promise+Async/awaitの例)

    var addCoffee = function (name) {
      return new Promise(function (resolve) {
        setTimeout(function () {
          resolve(name);
        }, 500);
      });
    };
    
    var coffeeMaker = async function () {
      var coffeeList = "";
      var _addCoffee = async function (name) {
        coffeeList += (coffeeList ? "," : "") + (await addCoffee(name));
      };
      await _addCoffee("에스프레소");
      console.log(coffeeList);
      await _addCoffee("아메리카노");
      console.log(coffeeList);
      await _addCoffee("카페모카");
      console.log(coffeeList);
      await _addCoffee("카페라떼");
      console.log(coffeeList);
    };
    coffeeMaker();
    ES 2017はasync/awaitを追加します.
    非同期操作を実行する関数の前にasyncをマークし、関数内部で実際の非同期操作が必要な位置にawaitをマークするだけで、後の内容を自動的にPromiseに変換し、その内容を解決してから次のステップに進むことができます.

    整理する

  • コールバック関数はパラメータとして他のコードに渡され、その制御権も一緒に委任された関数である.
  • の制御権を超えるコードには、次の制御権があります.
    呼び出し
  • コールバック関数の時点を自分で判断して実行する.
  • コールバック関数を呼び出すときにパラメータに渡される値とその順序を定義します.(この順序で行わないと思わぬ結果になります.)
  • コールバック関数を要求するthisが何であるかを規定する場合もある.
  • が確定していない場合は、グローバルオブジェクトが表示されます.
  • ユーザがこれを任意に変更したい場合はbindメソッドを用いることができる.
  • メソッドをパラメータとしてどの関数に渡しても、最終的には関数として実行されます.
  • の非同期制御のためにコールバック関数を使用すると、コールバック地獄に陥りやすい.
  • Promise、Generator、async/awaitなどcallback地獄から抜け出す方法が存在する.
  • 参考書:Core JavaScript