この翻訳は不完全です。英語から この記事を翻訳 してください。
概要
bind() メソッドは、呼び出された時に新しい関数を生成します。最初の引数 thisArg は新しい関数の this キーワードにセットされます。2 個目以降の引数は、新しい関数より前に、ターゲット関数の引数として与えられます。
構文
fun.bind(thisArg[, arg1[, arg2[, ...]]])
引数
thisArg- 束縛された関数が呼ばれる時、
this値としてターゲット関数に渡される値を指定します。束縛された関数がnew演算子によって構築された場合、この引数は無視されます。 arg1, arg2, ...- ターゲット関数を呼び出す時、束縛された関数に与えられる引数の前に付けてターゲット関数に渡す引数。
-
返り値
bindメソッドを通して与えられたthisやdefault引数が適用された、与えられた関数のコピーです。
説明
bind() 関数は、新たな関数(束縛された関数 = a bound function)を生成して返します。この新たな関数の本体(ECMAScript 5 の観点では内部の call プロパティ)は、call() 先の関数(束縛された関数のターゲット関数)【訳注: fun.bind(thisArg) の fun 】 と同じです。ターゲット関数の this の値が bind() に与えた第 1 引数になり(束縛され)、それを上書きすることはできません。bind() には、束縛された関数が呼ばれた時にターゲット関数に渡す、先頭部分のデフォルト引数も指定できます。また、束縛された関数を new 演算子を使用して新たなオブジェクトを構築することもできます。この場合、新しいオブジェクトは、あたかも(束縛された関数ではなく)ターゲット関数が構築されたかのように振る舞います。this 値を与えても無視されますが、それに続けて与えた引数は、エミュレートされた関数に渡されます。
例
束縛された関数を生成する
最もシンプルな bind() の使い方は、どのように呼び出された場合でも特定の this 値を持つ関数を生成することです。初心者の JavaScript プログラマーがよくやる間違いは、あるオブジェクトからメソッドを取り出し、後でその関数を呼び出すとき、その内側の this 値が元のオブジェクトになると考えてしまうことです(例えば、そのメソッドをコールバック関数に使うケース)。特に配慮しなければ、元のオブジェクトは失われてしまいます 【訳注: 取り出した関数内の this としては使えなくなる】。その関数に元々のオブジェクトを bind() して束縛された関数を生成すれば、この問題をきちんと解決することができます:
var x = 9;
var module = {
x: 81,
getX: function() { return this.x; }
};
module.getX(); // 81
var retrieveX = module.getX;
retrieveX();
// この関数はグローバルスコープで呼び出されるため9を返します。
// 'this' を module に結びつけた新しい関数を生成
var boundGetX = retrieveX.bind(module);
boundGetX(); // 81
部分的に適用された関数
TODO:
次にシンプルな bind() の使い方は、あらかじめ引数が指定された関数を生成することです。これらの引数は、this 値の後に続けます(指定しないことも可能)。すると、束縛された関数がいつ呼ばれても、この指定された引数を先頭にして束縛された関数の引数がターゲット関数に渡されます。
function list() {
return Array.prototype.slice.call(arguments);
}
var list1 = list(1, 2, 3); // [1, 2, 3]
// 先頭の引数がプリセットされた関数をつくる
var leadingThirtysevenList = list.bind(undefined, 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.ceil( 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!');
};
束縛された関数をコンストラクタとして使う
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"
var emptyObj = {};
var YAxisPoint = Point.bind(emptyObj, 0 /* x */);
var YAxisPoint = Point.bind(null,0 /*x*/); // doesn't support in the above polyfill
// works fine with native bind
var axisPoint = new YAxisPoint(5);
axisPoint.toString(); // "0,5"
axisPoint instanceof Point; // true
axisPoint instanceof YAxisPoint; // true
new Point(17, 42) instanceof YAxisPoint; // false
束縛された関数を new で使えるように生成するのに特別なことをする必要は無いので注意してください。当然、普通に呼び出される束縛された関数を生成する際も特別なことは必要ありません。もしその関数を new 演算子とともに呼び出すことにしか使いたくないと思っても、普通に呼び出すことはできてしまいます。
// この例は JavaScript コンソールで直接実行できます // ...上の例のつづき // 普通の関数としても実行できます //(あまり必要にはなりませんが) YAxisPoint(13); emptyObj.x + "," + emptyObj.y; // > "0,13"
束縛された関数を new でしか使えないように制限したい場合、または通常の呼び出しだけに制限したい場合には、ターゲット関数がその制限を強制するようにしなければなりません。
ショートカットを作成する
bind() は、特定の this を必須とするような関数のショートカットを作成するのにも便利です。
例として、Array.prototype.slice を取り上げます。この関数は、配列に似たオブジェクトを本物の配列へ変換するために使えます。まず、次のようにショートカットを作成するとします:
var slice = Array.prototype.slice; // ... slice.call(arguments);
bind() を使うと、さらにシンプルにできます。次のコードでは、slice が Function.prototype.call() 関数に結びつけられた関数になり、その内側の this 値は Array.prototype.slice() 関数になります。こうすると、いちいち call() を呼び出す必要がなくなります:
var unboundSlice = Array.prototype.slice; // ひとつ前の例の "slice" と同じ var slice = Function.prototype.call.bind(unboundSlice); // ... slice(arguments);
Polyfill
bind 関数は ECMA-262 第5版に最近追加されたので、すべてのブラウザに存在するわけではありません。以下のコードをあなたのスクリプトの先頭に挿入すれば、その状況をいくらか変えることができます。ネイティブでサポートされていない実装において、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 && oThis
? this
: oThis,
aArgs.concat(Array.prototype.slice.call(arguments)));
};
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, delegate の際にTypeErrorを投げる【訳注: strict mode での話をしているようだ】)を持たない(Object.definePropertyをサポートする実装の上ではこれを追加することができる。あるいは__defineGetter__と__defineSetter__をサポートする実装上でも部分的に実装可能だが、delegate の際に例外を投げるようにはできない)。 - この不完全な実装は
prototpyeプロパティを持つ関数をつくり出す(正規の束縛された関数には存在しない)。 - この不完全な実装でつくられた束縛された関数の
lengthプロパティは、ECMA-262 で示されているものと一致しない。この実装がつくる関数のlengthは常に 0 だが、完全な実装においては、ターゲット関数と先行定義引数の長さ次第で、0 ではない長さになりうる。
この不完全な実装を使うことを選ぶ場合は、ECMA-262 第5版の定義から外れる挙動に依存しないように! ただし、いくらか気をつければ(特定の要望に適するような追加修正も必要かもしれません)、この不完全な実装は、bind() が広く仕様通りに実装されるまでの悪くないつなぎとして使えるでしょう 【訳注: Prototype.js の Function#bind、jQuery の jQeury.proxy()、Underscore.js の _.bind() など多数のライブラリでも提供されています】。
仕様
| 仕様 | 状態 | コメント |
|---|---|---|
| ECMAScript 5.1 (ECMA-262) Function.prototype.bind の定義 |
標準 | Initial definition. Implemented in JavaScript 1.8.5. |
| ECMAScript 2015 (6th Edition, ECMA-262) Function.prototype.bind の定義 |
標準 | |
| ECMAScript 2017 Draft (ECMA-262) Function.prototype.bind の定義 |
ドラフト |
ブラウザ互換性
| 機能 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
|---|---|---|---|---|---|
| 基本サポート | 7 | 4.0 (2) | 9 | 11.60 | 5.1.4 |
| 機能 | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
|---|---|---|---|---|---|---|
| 基本サポート | 4.0 | 0.16 | 4.0 (2) | ? | 11.50 | 6.0 |
Based on Kangax's compat tables.