Function.prototype.bind()
BaselineWidely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since July 2015.
bind()
はFunction
インスタンスのメソッドで、新しい関数を生成し、呼び出し時に、this
キーワードを指定された値に設定し、指定された引数の並びを、新しい関数が呼び出された際に指定されたものより前にして呼び出します。
試してみましょう
const module = { x: 42, getX: function () { return this.x; },};const unboundGetX = module.getX;console.log(unboundGetX()); // グローバルスコープで関数を呼び出す// 期待される出力結果: undefinedconst boundGetX = unboundGetX.bind(module);console.log(boundGetX());// 期待される出力結果: 42
構文
bind(thisArg)bind(thisArg, arg1)bind(thisArg, arg1, arg2)bind(thisArg, arg1, arg2, /* …, */ argN)
引数
返値
this
の値と初期の引数(提供された場合)が指定された関数のコピーです。
解説
bind()
関数は新しい「バインド済み関数 (bound function)」を生成します。バインド済み関数を呼び出すと、通常はラップされた関数のほうが実行され、それは「ターゲット関数 (target function)」とも呼ばれます。バインド済み関数は、渡された引数、すなわちthis
の値と最初のいくつかの引数を内部の状態として格納します。これらの値は、呼び出し時に渡されるのではなく、あらかじめ格納されています。一般に、const boundFn = fn.bind(thisArg, arg1, arg2)
は、const boundFn = (...restArgs) => fn.call(thisArg, arg1, arg2, ...restArgs)
と呼ばれるのと同じだと考えてよいでしょう(ただしboundFn
が構築されたときではなく、呼び出されたときに効果があります)。
バインド済み関数は、boundFn.bind(thisArg, /* その他の引数 */)
を呼び出すことでさらにバインドすることができ、別のバインド済み関数boundFn2
が作成されます。なぜなら、boundFn2
の対象となる関数boundFn
はすでにthis
というバインド済み関数を持っているからです。boundFn2
が呼ばれると、boundFn
を呼び出すことになり、それがfn
を呼び出すことになります。最終的にfn
が受け取る引数は、順にboundFn
にバインドされた引数、boundFn2
にバインドされた引数、boundFn2
で受け取った引数になります。
"use strict"; // `this` がラッパーオブジェクトの中に入ってしまうのを防ぐfunction log(...args) { console.log(this, ...args);}const boundLog = log.bind("this value", 1, 2);const boundLog2 = boundLog.bind("new this value", 3, 4);boundLog2(5, 6); // "this value", 1, 2, 3, 4, 5, 6
バインド済み関数は、そのターゲット関数が構築可能であれば、new
演算子を使用してオブジェクトを構築することもできます。これは、あたかもターゲット関数で構築されたかのように動作します。前置された引数は通常通りターゲット関数に提供されますが、指定されたthis
値は無視されます(コンストラクターはReflect.construct
の引数で見られるように、自分自身でthis
を用意するためです)。バインド済み関数が直接構築された場合、代わりにnew.target
がターゲット関数になります(つまり、バインドされた関数はnew.target
に対して透過的です)。
class Base { constructor(...args) { console.log(new.target === Base); console.log(args); }}const BoundBase = Base.bind(null, 1, 2);new BoundBase(3, 4); // true, [1, 2, 3, 4]
しかし、バインド済み関数はprototype
プロパティを持たないので、extends
の基底クラスとして使用することはできません。
class Derived extends class {}.bind(null) {}// TypeError: Class extends value does not have valid prototype property undefined
バインド済み関数をinstanceof
の右辺として使用する場合、instanceof
はターゲット関数(これはバインド済み関数の内部に格納されています)に到達し、 代わりにそのprototype
を読み取ります。
class Base {}const BoundBase = Base.bind(null, 1, 2);console.log(new Base() instanceof BoundBase); // true
バインド済み関数には以下のようなプロパティがあります。
length
ターゲット関数の
length
から、バインドされる引数の数(thisArg
引数はカウントしない)を引いた値で、0が最小値になります。name
対象となる関数の
name
に"bound "
という接頭辞を加えたものです。
バインド済み関数は、ターゲット関数のプロトタイプチェーンも引き継ぎます。しかし、他にターゲット関数の独自のプロパティ(ターゲット関数がクラスの場合は静的プロパティなど)を持つことはありません。
例
バインド済み関数の生成
最もよくあるbind()
の使い方は、どのように呼び出された場合でも特定のthis
値を持つ関数を生成することです。
初心者の JavaScript プログラマーがよくやる間違いは、あるオブジェクトからメソッドを抽出し、後でその関数を呼び出すとき、その内側のthis
値が元のオブジェクトになると考えてしまうことです(例えば、そのメソッドをコールバック関数に使う場合)。
特に配慮しなければ、ふつうは元のオブジェクトが見えなくなります。その関数に元々のオブジェクトをbind()
してバインド済み関数を生成すれば、この問題をきちんと解決することができます。
// 最上位の 'this' はスクリプト内で 'globalThis' に結び付けられるthis.x = 9;const module = { x: 81, getX() { return this.x; },};// 'getX' の 'this' 引数が 'module' に結び付けられるconsole.log(module.getX()); // 81const retrieveX = module.getX;// 'retrieveX' の 'this' 引数は、非厳格モードでは 'globalThis' に結び付けられるconsole.log(retrieveX()); // 9// 新しい関数 'boundGetX' を作成し、 'this' 引数を 'module' に結びつけるconst boundGetX = retrieveX.bind(module);console.log(boundGetX()); // 81
メモ:この例を厳格モードで実行すると、retrieveX
のthis
引数はglobalThis
ではなくundefined
に結び付けられ、retrieveX()
の呼び出しは失敗します。
この例を ECMAScript モジュールで実行すると、最上位のthis
はglobalThis
ではなくundefined
に結び付けられており、this.x = 9
の代入が失敗します。
これを Node CommonJS モジュール内で実行すると、厳格モードかどうかに関わらず、最上位のthis
はglobalThis
ではなくmodule.exports
を指すようになります。しかし、関数内では、バインドされていないthis
の参照は、それでも「厳格モードでなければglobalThis
、厳格モードならばundefined
」というルールに従います。したがって、厳格モードでない場合(既定)は、retrieveX()
はundefined
を返します。これは、this.x = 9
が、getX
が読んでいるオブジェクト (globalThis
) とは異なるオブジェクト (module.exports
) に書き込んでいるためです。
実際、いくつかの組み込みの「メソッド」はバインド済み関数を返すゲッターでもあります。注目すべき例はIntl.NumberFormat.prototype.format()
で、アクセスすると、コールバックとして直接渡すことのできる、バインド済み関数を返します。
部分的に適用された関数
もう一つのbind()
の使い方は、あらかじめ引数が指定された関数を生成することです。
これらの引数は(もしあれば)指定されたthis
値の後に続き、ターゲット関数に渡される引数の先頭に挿入され、その後に、バインドされた関数が呼ばれる際に渡される引数が続きます。
function list(...args) { return args;}function addArguments(arg1, arg2) { return arg1 + arg2;}console.log(list(1, 2, 3)); // [1, 2, 3]console.log(addArguments(1, 2)); // 3// 先頭の引数が設定済みの関数を生成します。const leadingThirtySevenList = list.bind(null, 37);// 第一引数が設定済みの関数を生成します。const addThirtySeven = addArguments.bind(null, 37);console.log(leadingThirtySevenList()); // [37]console.log(leadingThirtySevenList(1, 2, 3)); // [37, 1, 2, 3]console.log(addThirtySeven(5)); // 42console.log(addThirtySeven(5, 10)); // 42// (最後の引数 10 は無視されます)
setTimeout() での利用
既定では、setTimeout()
内部のthis
キーワードはglobalThis
、すなわちブラウザーではwindow
に設定されます。クラスメソッドを使用してthis
がクラスインスタンスを参照するようにする必要がある場合、インスタンスを保守するために、明示的にthis
をコールバック関数にバインドすることができます。
class LateBloomer { constructor() { this.petalCount = Math.floor(Math.random() * 12) + 1; } bloom() { // 1 秒間の遅延の後に開花を宣言 setTimeout(this.declare.bind(this), 1000); } declare() { console.log(`わたしは花びらが ${this.petalCount} 枚の綺麗な花です!`); }}const flower = new LateBloomer();flower.bloom();// After 1 second, calls 'flower.declare()'
このためにアロー関数を使用することもできます。
class LateBloomer { bloom() { // 1 秒間の遅延の後に開花を宣言 setTimeout(() => this.declare(), 1000); }}
コンストラクターとして使用するバインド済み関数
バインド済み関数は自動的に、new
演算子を使ってターゲット関数の新しいインスタンスを構築できるようになっています。新たな値を構築するためにバインド済み関数を使った場合、this
を与えても無視されます。しかし、同時に与える引数はコンストラクター呼び出しの先頭部分に挿入されます。
function Point(x, y) { this.x = x; this.y = y;}Point.prototype.toString = function () { return `${this.x},${this.y}`;};const p = new Point(1, 2);p.toString();// '1,2'// thisArg の値は無視されるため、重要ではないconst YAxisPoint = Point.bind(null, 0 /*x*/);const axisPoint = new YAxisPoint(5);axisPoint.toString(); // '0,5'axisPoint instanceof Point; // trueaxisPoint instanceof YAxisPoint; // truenew YAxisPoint(17, 42) instanceof Point; // true
new
で使用するためのバインド済み関数を作成するために、特別なことをする必要はないことに注意してください。new.target
,instanceof
,this
などはすべて期待通り、まるでこのコンストラクターがバインドされていないかのように処理されます。唯一の異なる形は、extends
には使用できなくなるということです。
このことから、たとえバインド済み関数がnew
を使用してのみ呼び出すことを要求したい場合でも、プレーンに呼び出される関数を作成することと比べて、特別なことをする必要はないことがわかります。new
を使わずに呼び出すと、バインドされたthis
は突然無視されなくなります。
const emptyObj = {};const YAxisPoint = Point.bind(emptyObj, 0 /*x*/);// 普通の関数としても実行できます// (あまり必要にはなりませんが)YAxisPoint(13);// これで `this` に対する変更が外から監視できるようになりました。console.log(emptyObj); // { x: 0, y: 13 }
バインドされた関数がnew
でしか呼び出せないように制限したい場合、あるいはnew
なしでのみ呼び出せるようにしたい場合、ターゲット関数はnew.target !== undefined
を参照するか、代わりにクラスを使用して、その制限を実施しなければなりません。
クラスのバインド
クラスに対してbind()
を使用すると、そのクラスの意味づけはほとんど維持されますが、現在のクラスのすべての静的な自分自身のプロパティは失われます。しかし、プロトタイプチェーンは維持されるので、親クラスから継承された静的プロパティにアクセスすることは可能です。
class Base { static baseProp = "base";}class Derived extends Base { static derivedProp = "derived";}const BoundDerived = Derived.bind(null);console.log(BoundDerived.baseProp); // "base"console.log(BoundDerived.derivedProp); // undefinedconsole.log(new BoundDerived() instanceof Derived); // true
メソッドのユーティリティ関数への変換
bind()
は特定のthis
値を必要とするメソッドを、前回のthis
引数を通常の引数として受け入れるプレーンなユーティリティ関数に変換したい場合にも役立ちます。これは、汎用的なユーティリティ関数の動作方法に似ています。array.map(callback)
を呼び出す代わりに、map(array, callback)
を使うと、配列でない配列風オブジェクト(例えばarguments
など)でも、Object.prototype
を変更せずにmap
が使用できるようになります。
例として、Array.prototype.slice()
を取り上げます。この関数は、配列に似たオブジェクトを本物の配列へ変換するために使えます。まず、次のようにショートカットを作成するとします。
const slice = Array.prototype.slice;// ...slice.call(arguments);
slice.call
を保存して、普通の関数として呼び出すことはできないことに注意してください。なぜなら、call()
メソッドは、呼び出すべき関数であるthis
の値も読み取るからです。この場合、bind()
を使用して、call()
にthis
の値をバインドすることができます。以下のコードでは、slice()
はFunction.prototype.call()
のバインド版で、this
の値はArray.prototype.slice()
にバインドされます。これは、追加のcall()
呼び出しを省くことができることを意味しています。
// ひとつ前の例の "slice" と同じconst unboundSlice = Array.prototype.slice;const slice = Function.prototype.call.bind(unboundSlice);// ...slice(arguments);
仕様書
Specification |
---|
ECMAScript® 2026 Language Specification # sec-function.prototype.bind |