tech

テックカテゴリー

JSで忘れがちなcall() / apply() / bind()について

Category | front

Tag | JavaScript

Author | naohito-T

Post | 2022-12-26 / 34,254views

今回は、最近 JavaScriptを書き始めた初心者の方や、ES6 以降の JavaScriptしか触っていない方などに向けて

Function.prototype.call() / Function.prototype.apply() / Function.prototype.bind() について紹介します。
また様々なライブラリを使用しプロジェクトを進めていくと思いますが、ライブラリが動かないなど原因を調査する際にピュアなJSを見る場面が多く、僕自身も都度忘れるため備忘録を兼ねた記事です。
※この記事はJavaScriptでのthisの扱いを理解しておく必要があります。以下の記事でthisについて記載してます。

では本題です

前提

Function.prototype.call() / Function.prototype.apply() / Function.prototype.bind() はすべて関数オブジェクトのメソッドとなります。
関数として定義したものは、すべてこの三つのメソッドを呼び出せます。
ではこのメソッドが必要な理由とは?
答えは以下です。
関数のthisはグローバルオブジェクトで、ブラウザの場合はWindowオブジェクトです。
ちなみにstrictモードの場合、thisにはundefinedがセットされます。
これが厄介でした。そのためthisを固定したい要望が生まれ上記のメソッド群が生まれたわけです。

call()とは

// 構文
func.call([thisArg[, arg1, arg2, ...argN]])

既存の関数を呼び出す際に、異なるthisオブジェクトを割り当てることが可能になります。

apply()とは

既存の関数を呼び出す際に、異なるthisオブジェクトを割り当てることが可能になります(callとの違いは...?)

Tips

call()メソッドは、第二引数以降にメソッドに渡す引数を設定できるのですが、スプレッド構文を使うことで配列もいれることができます。そしてその結果はapply()メソッドと全く同じになるため、apply()メソッドは近年使われなくなりつつあります。

call()とapply()の違い

call()はbind()と同じく、第二引数以下は関数の引数を文字列で指定できます。
bind()とよく似ており、異なる点としては呼び出し時に実行される点だけです。
apply()は、第二引数には文字列はとれず、配列となります。
それぞれの違いは次の通りです。

function hello(name1, name2, name3) {
  console.log("こんにちは、" + name1 + " " + name2 + " " + name3);
}
const tanaka = { name: "tanaka" };

// bindの場合
const bindTanaka = hello.bind(null, tanaka.name, "sasaki", "saito");
bindTanaka(tanaka); //こんにちは、tanaka sasaki saito

// callの場合
hello.call(tanaka, tanaka.name, "sasaki", "saito"); //こんにちは、tanaka sasaki saito

// applyの場合
hello.apply(tanaka, [tanaka.name, "sasaki", "saito"]); //こんにちは、tanaka sasaki saito

※引数が配列ならapply()、文字列ならcall()を使うといった使い分けでOKです。
またbindの第一引数のthisキーワード部分は、なにかいれる必要があるので、thisを入れない場合は一般的にはnullが使われます(第一引数にnullやundefinedを指定すると、thisはグローバルオブジェクト(ブラウザはwindowオブジェクト)となります。)

bind()とは

bind()メソッドはthisや引数を固定した新しい関数を作成します。
bind()メソッドの第一引数として指定したオブジェクトをthisの参照先として固定することができます。

かつては現在ほど簡単ではなかった実行コンテキスト、あるいはthis の扱いをぐっと容易にしたとても便利な関数です。

現在はアロー関数の登場により使う機会はほぼなくなっているもののpure JSをコードリーディングするには欠かせない知識です。


次の例を見てみましょう

function hello() {
  console.log("こんにちは、" + this.name);
}
const tanaka = hello.bind({ name: "tanaka" });

tanaka("sasaki"); // こんにちは、tanaka

呼び出す関数で引数を指定したとしても、bindの第二引数で引数を固定している場合、bindが指定した引数が優先されます。(引数を予約していると考えるとわかりやすいです)
また、このようにbindによる固定をbindによるthisの束縛といいます。

bind(), call(), apply()の違い

bind()はthisキーワードや引数の参照先を変更します。
使用時点で実行はしません。

call(), apply()はthisキーワードや引数の参照先を変更する点はbind()と同じです。
異なる点は、使用時点で関数を実行することです。
次の例を見てみましょう

function hello() {
  console.log("こんにちは、" + this.name);
}
const tanaka = { name: "tanaka" };

// bindの場合
const bindTanaka = hello.bind(tanaka);
bindTanaka(); //こんにちは、tanaka

// call()の場合
hello.call(tanaka); //こんにちは、tanaka

// apply()の場合
hello.apply(tanaka); //こんにちは、tanaka


このように、bind()だけthisを固定、呼び出しの二段階となります。

まとめ

JavaScriptではthis の扱いが特殊なためこういったメソッドを使用し固定する場面が必要な時があります。
こういった根本を知っていることでよりTypeScriptのAltJSなど使うときに応用が効きます。

この記事は以上となります。

Share this article

naohito-T

naohito-T

千葉出身。都内(銀座)での美容師経験があるソフトウェアエンジニア。モデル撮影のために購入したmacを使っていたらPCの仕組みが気になり始め、気づいたらエンジニアに。人生まるごとクリエイティブだと思っている。人の髪をデザインしていた経験をIT業界へ活かせることはないか日々模索中。