bind() メソッドは新しい関数を生成し、その関数が呼び出された時の一連の引数の前に、提供された値が設定された this キーワードが追加されて呼び出されます。
このデモのソースファイルは GitHub リポジトリに格納されています。デモプロジェクトに協力したい場合は、 https://github.com/mdn/interactive-examples をクローンしてプルリクエストを送信してください。
構文
function.bind(thisArg[, arg1[, arg2[, ...]]])
引数
thisArg- バインドされた関数が呼び出される際、ターゲット関数に
this引数として渡される値です。バインドされた関数がnew演算子によって構築された場合、この引数は無視されます。setTimeoutの中でbindを使用して (コールバックとして提供する) 関数を生成する場合、thisArgとして渡されるプリミティブ値はオブジェクトに変換されます。bindに引数が提供されない場合は、実行スコープのthisは新しい関数のためのthisArgとして扱われます。 arg1, arg2, ...- ターゲット関数を呼び出す時、バインドされた関数に与えられる引数の前に付けてターゲット関数に渡す引数。
返値
this の値と初期の引数を指定された関数のコピーです。
説明
bind() 関数は、新しいバインドされた関数 (BF) を生成します。 BF は特殊関数オブジェクト (exotic function object; ECMAScript 2015からの用語) であり、元の関数オブジェクトをラップします。BF を呼び出すとラップされた関数が実行されます。
BF は下記の内部プロパティを持ちます。
- [[BoundTargetFunction]] - ラップされた関数オブジェクト;
- [[BoundThis]] - ラップされた関数を呼び出す時に常に this に渡される値。
- [[BoundArguments]] - ラップされた関数を呼び出す時に、その要素が第1引数として使われる値のリスト。
- [[Call]] - オブジェクトに関連する実行コード。関数呼び出し式を通じて実行される。内部メソッドへの引数は this 値と呼び出し式によって関数に渡される引数を含むリスト。
バインドされた関数が呼ばれるとき、[[BoundTargetFunction]]上の内部メソッド [[Call]] を、次の引数とともに呼び出します: Call(boundThis, args)。ここで boundThis は [[BoundThis]]、args は [[BoundArguments]] で、後に関数呼び出しで渡された引数が続きます。
バインドされた関数は new 演算子でも生成されます。これを行うとターゲット関数が代わりに生成されたようになります。与えられた this の値は無視され、追加された引数はエミュレートされた関数に提供されます。
例
バインドされた関数の生成
最もシンプルな bind() の使い方は、どのように呼び出された場合でも特定の this 値を持つ関数を生成することです。初心者の JavaScript プログラマーがよくやる間違いは、あるオブジェクトからメソッドを取り出し、後でその関数を呼び出すとき、その内側の this 値が元のオブジェクトになると考えてしまうことです (例えば、そのメソッドをコールバック関数に使うケース)。特に配慮しなければ、元のオブジェクトは失われてしまいます 【訳注: 取り出した関数内の this としては使えなくなる】。その関数に元々のオブジェクトを bind() して束縛された関数を生成すれば、この問題をきちんと解決することができます。
this.x = 9; // this はここではブラウザーのグローバルな "window" オブジェクト
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// この関数はグローバルスコープで呼び出されるため9を返します。
// 'this' を module に結びつけた新しい関数を生成
// 初心者のプログラマーはグローバル変数の x と
// モジュールプロパティの x とを混同するかもしれません。
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
部分的に適用された関数
次にシンプルな bind() の使い方は、あらかじめ引数が指定された関数を生成することです。これらの引数は、this 値の後に続けます (指定しないことも可能)。すると、束縛された関数がいつ呼ばれても、この指定された引数を先頭にして束縛された関数の引数がターゲット関数に渡されます。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 先頭の引数がプリセットされた関数をつくる
var leadingThirtysevenList = list.bind(null, 37);
var list2 = leadingThirtysevenList();
// [37]
var list3 = leadingThirtysevenList(1, 2, 3);
// [37, 1, 2, 3]
setTimeout とともに
既定では、 window.setTimeout() 内部の this キーワードは window (あるいは global オブジェクト) に設定されます。クラスインスタンスを参照する this が必要なクラスメソッドを使う場合、 this をコールバック関数と明確に結びつけて(バインドして)、インスタンスを維持することができます。
function LateBloomer() {
this.petalCount = Math.floor(Math.random() * 12) + 1;
}
// 1 秒遅延させてから bloom を宣言する
LateBloomer.prototype.bloom = function() {
window.setTimeout(this.declare.bind(this), 1000);
};
LateBloomer.prototype.declare = function() {
console.log('I am a beautiful flower with ' +
this.petalCount + ' petals!');
};
var flower = new LateBloomer();
flower.bloom();
// after 1 second, triggers the 'declare' method
コンストラクターとして使用するバインドされた関数
警告: このセクションでは、JavaScript の性能 (capabilities) を実演して bind() メソッドの極端な例を説明していますが、以下の方法がベストなわけではなく、むしろ本番環境では推奨されない方法です。
バインドされた関数は自動的に、 new 演算子を使ってターゲット関数の新しいインスタンスを構築できるようになっています。新たな値を構築するために束縛された関数を使った場合、 this を与えても無視されます。しかし、同時に与える引数はコンストラクター呼び出しの先頭部分に挿入されます:
function Point(x, y) {
this.x = x;
this.y = y;
}
Point.prototype.toString = function() {
return this.x + ',' + this.y;
};
var p = new Point(1, 2);
p.toString(); // '1,2'
// not supported in the polyfill below,
// works fine with native bind:
var YAxisPoint = Point.bind(null, 0/*x*/);
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0/*x*/);
var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // '0,5'
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // true
バインドされた関数を new で使えるように生成するのに特別なことをする必要は無いので注意してください。当然、普通に呼び出されるバインドされた関数を生成する際も特別なことは必要ありません。もしその関数を new 演算子とともに呼び出すことにしか使いたくないと思っても、普通に呼び出すことはできてしまいます。
// この例は JavaScript コンソールで直接実行できます // ...上の例のつづき // 普通の関数としても実行できます // (あまり必要にはなりませんが) YAxisPoint(13); emptyObj.x + ',' + emptyObj.y; // > '0,13'
バインドされた関数を new でしか使えないように制限したい場合、または通常の呼び出しだけに制限したい場合には、ターゲット関数がその制限を強制するようにしなければなりません。
ショートカットを作成する
bind() は、特定の this を必須とするような関数のショートカットを作成するのにも便利です。
例として、Array.prototype.slice を取り上げます。この関数は、配列に似たオブジェクトを本物の配列へ変換するために使えます。まず、次のようにショートカットを作成するとします。
var slice = Array.prototype.slice; // ... slice.apply(arguments);
bind() を使うと、さらにシンプルにできます。次のコードでは、 slice が Function.prototype の apply() 関数に結びつけられた関数になり、その内側の this 値は Array.prototype のslice() 関数にセットされます。こうすると、いちいち apply() を呼び出す必要がなくなります。
// ひとつ前の例の "slice" と同じ var unboundSlice = Array.prototype.slice; var slice = Function.prototype.apply.bind(unboundSlice); // ... slice(arguments);
Polyfill
以下のコードをあなたのスクリプトの先頭に挿入すれば、その状況をいくらか変えることができます。ネイティブで対応されていない実装において、 bind() の多くの機能を使えるようになります。
if (!Function.prototype.bind) {
Function.prototype.bind = function(oThis) {
if (typeof this !== 'function') {
// closest thing possible to the ECMAScript 5
// internal IsCallable function
throw new TypeError('Function.prototype.bind - what is trying to be bound is not callable');
}
var aArgs = Array.prototype.slice.call(arguments, 1),
fToBind = this,
fNOP = function() {},
fBound = function() {
return fToBind.apply(this instanceof fNOP
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
if (this.prototype) {
// Function.prototype doesn't have a prototype property
fNOP.prototype = this.prototype;
}
fBound.prototype = new fNOP();
return fBound;
};
}
このアルゴリズムと仕様上のアルゴリズムとの間には、いくつか大きな違いがあります (真剣に網羅することを目指したわけではないので、他にも差はあるかもしれません)。
- この部分的な実装は、
Array.prototype.slice(),Array.prototype.concat(),Function.prototype.call(),Function.prototype.apply()という、それぞれオリジナルの値を持つ組み込みメソッドに依存している。 - この不完全な実装がつくり出す関数は、不変の (immutable) "poison pill"
callerプロパティとargumentsプロパティ (get, set, delete の際にTypeErrorを投げる【訳注: strict mode での話をしているようだ】) を持たない (Object.definePropertyをサポートする実装の上ではこれを追加することができる。あるいは__defineGetter__と__defineSetter__に対応する実装上でも部分的に実装可能だが、 delete の際に例外を投げるようにはできない)。 - この不完全な実装は
prototypeプロパティを持つ関数をつくり出す (正規の束縛された関数には存在しない)。 - この不完全な実装でつくられた束縛された関数の
lengthプロパティは、 ECMA-262 で示されているものと一致しない。この実装がつくる関数のlengthは常に 0 だが、完全な実装においては、ターゲット関数と先行定義引数の長さ次第で、0 ではない長さになりうる。
この不完全な実装を使うことを選ぶ場合は、 ECMA-262 第5版の定義から外れる挙動に依存しないように! ただし、いくらか気をつければ(特定の要望に適するような追加修正も必要かもしれません)、この不完全な実装は、bind() が広く仕様通りに実装されるまでの悪くないつなぎとして使えるでしょう 【訳注: Prototype.js の Function#bind、jQuery の jQeury.proxy()、 Underscore.js の _.bind() など多数のライブラリでも提供されています】。
もっと完全な解決法は https://github.com/Raynos/function-bind を確認してください
仕様書
| 仕様書 | 状態 | コメント |
|---|---|---|
| ECMAScript 5.1 (ECMA-262) Function.prototype.bind の定義 |
標準 | 初回定義。 JavaScript 1.8.5 で実装。 |
| ECMAScript 2015 (6th Edition, ECMA-262) Function.prototype.bind の定義 |
標準 | |
| ECMAScript Latest Draft (ECMA-262) Function.prototype.bind の定義 |
ドラフト |
ブラウザーの対応
このページの互換性一覧表は構造化データから生成されています。データに協力していただけるのであれば、 https://github.com/mdn/browser-compat-data をチェックアウトしてプルリクエストを送信してください。
| デスクトップ | モバイル | サーバー | |||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bind | Chrome 完全対応 7 | Edge 完全対応 12 | Firefox 完全対応 4 | IE 完全対応 9 | Opera 完全対応 11.6 | Safari 完全対応 5.1 | WebView Android 完全対応 4 | Chrome Android 完全対応 18 | Firefox Android 完全対応 4 | Opera Android 完全対応 12 | Safari iOS 完全対応 6 | Samsung Internet Android 完全対応 1.0 | nodejs 完全対応 あり |
凡例
- 完全対応
- 完全対応