JavaScript アドバンス

JavaScript の bind() メソッド

1. メソッド借用 (Method Borrowing)

call()apply() と同様に、bind() メソッドを使用すると、他のオブジェクトからメソッドを借用(Borrow)することができます。

ただし、call()apply() とは決定的な違いがあります。bind() メソッドは 関数を即座に実行しません。
その代わりに、後で呼び出すことができる「新しい関数」を返します。
この新しい関数は、あなたが指定した this の値を永続的に保持(記憶)します。

bind() はアドバンスド(高度)なトピックです。続行する前に、thiscall()、および apply() を理解していることを確認してください。

2. bind() の基本構文

bind() メソッドは新しい関数を作成します。

  • 第1引数で、新しい関数のための this の値を設定します。
  • 第2引数以降は、新しい関数における 固定された引数 となります。

2.1 構文

const newFunction = functionName.bind(thisArg, arg1, arg2, ...);

3. this を固定するための bind() 使用法

bind() の最も一般的な用途は、関数が常に同じ this 値を使用するように保証することです。

3.1 実装例

const person1 = { name: "ジョン" };
const person2 = { name: "ポール" };
const person3 = { name: "リンゴ" };

function greet() {
  return "こんにちは " + this.name;
}

// this を person1 に固定した新しい関数を作成
const greetJohn = greet.bind(person1);

greetJohn(); // "こんにちは ジョン" を返す

greetJohn は、常に person1this として使用する新しい関数です。

3.2 オブジェクト間でのメソッド借用

以下の例では、personmember という2つのオブジェクトを作成しています。member オブジェクトが person オブジェクトから fullName メソッドを借用します。

// person オブジェクトを作成
const person = {
  firstName: "ジョン",
  lastName: "ドウ",
  fullName: function () {
    return this.firstName + " " + this.lastName;
  }
}

// member オブジェクトを作成
const member = {
  firstName: "ヘゲ",
  lastName: "ニルセン",
}

// fullName メソッドを member オブジェクトにバインド(束縛)
let fullName = person.fullName.bind(member);

// 後で fullName() を呼び出す
fullName(); // "ヘゲ ニルセン"

4. bind() vs call() と apply()

これらのメソッドの違いを理解することは非常に重要です。

  • call(): 関数を 即座に 呼び出す
  • apply(): 関数を 即座に 呼び出す
  • bind(): 新しい関数を返す

4.1 比較例

// 関数を即座に呼び出す
greet.call(person);

// 関数を即座に呼び出す
greet.apply(person);

// 新しい関数を作成する(実行はされない)
const greetLater = greet.bind(person);

// 後で新しい関数を呼び出す
greetLater();

5. 後で呼び出される関数のための bind()

bind() を使用しないと、this の値が失われてしまう(意図しないものに変わる)ことがあります。

5.1 this が失われる例

const person = {
  name: "ジョン",
  sayHello: function() {
    return "こんにちは " + this.name;
  }
};

// メソッドを変数に代入(ここで this のコンテキストが切り離される)
const hello = person.sayHello;
hello(); // this は person ではないため、期待通りに動作しない

5.2 bind() による解決

// person に bind することで、コンテキストを固定する
const hello = person.sayHello.bind(person);
hello(); // 正常に動作する

6. this を維持するための bind() 利用

bind() メソッドは、this の消失を防ぐ ために使用できます。
以下の例では、person オブジェクトに display メソッドがあります。このメソッド内では、thisperson オブジェクトを参照しています。

const person = {
  firstName: "ジョン",
  lastName: "ドウ",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

person.display();

しかし、関数を コールバック(Callback) として使用すると、this は失われます。
例えば、setTimeout() の中で person.display を使用してみましょう。

6.1 this が消失するケース(setTimeout)

// これは名前の代わりに undefined を表示します
const person = {
  firstName: "ジョン",
  lastName: "ドウ",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

// 3秒後に実行される際、this が person を指さなくなっている
setTimeout(person.display, 3000);

6.2 bind() による解決策

bind() メソッドはこの問題を解決します。

// 正しく表示されます
const person = {
  firstName: "ジョン",
  lastName: "ドウ",
  display: function () {
    let x = document.getElementById("demo");
    x.innerHTML = this.firstName + " " + this.lastName;
  }
}

// person.display を person オブジェクトに bind する
let display = person.display.bind(person);
setTimeout(display, 3000);

7. 引数を伴う bind()

bind() に渡された引数は、新しい関数における 固定された値 になります。
これは 部分適用 (Partial Application) と呼ばれることもあります。

7.1 実装例

function multiply(a, b) {
  return a * b;
}

// 第1引数 a を 2 に固定した新しい関数を作成
const double = multiply.bind(null, 2);

double(5); // 2 * 5 = 10 を返す

この double 関数は、常に最初の引数として 2 を使用するようになります。

8. bind() は元の関数を変更しない

bind() を実行しても、元の関数自体が書き換えられることはありません。
bind() を呼び出すたびに、常に 新しい関数 が生成されます。

8.1 実装例

// それぞれ異なる this を持った新しい関数が生成される
const greetJohn = greet.bind({ name: "ジョン" });
const greetAnna = greet.bind({ name: "アンナ" });