.bind()

呼び出されると新しい関数を作成し、その新しい関数が呼び出されると、thisに提供された値をセットし、その後に続く引数とともに実行されます。

文法

fun.bind(thisArg[, arg1[, arg2[, ...]]])
引数 説明
thisArg バインドされた関数が呼び出された際に、 対象となった関数(fun)へthisパラメーターとして渡される値を指定します。 バインドされた関数がnew演算子を使用してコンストラクタ処理された場合、この値は無視されます。
arg1, arg2, ... 対象となった関数が呼び出された際に、バインドされた関数へ従来の引数の前に差し込む引数として提供されます。

bind()関数は、同じ関数本文(ECMAScript 5観点で、 内部のcallプロパティ)を持つ新しい関数(バインドされた関数)を作成します。 この新しい関数は、(バインドされた関数の対象となった関数上で)呼び出された際に、 bind()の第1引数をthis値にバインドし、これを上書きすることは出来ません。 またbind()は、バインドされた関数が呼び出される際に、 対象の関数へ提供されるデフォルトの引数の読み込みを受け入れます。

バインドされた関数は、new演算子を使用してコンストラクタ処理することも可能で、 これは対象となった関数がコンストラクタ処理されたように振る舞われます。 この場合、提供されたthis値は無視されますが、 それに続く引数はエミュレートされた関数へ提供されます。

バインドされた関数の作成

bind()の最も分かりやすい使用方法は、どのような呼び出され方をしたとしても、 特定のthis値が使用される関数を作ることです。 多くのJavaScriptの初心者プログラマーが陥りやすい間違いに、 オブジェクからのメソッドを抜き出しておき、 後ほどthis値に元のオブジェクトが使用されることを期待して、 その関数(メソッド)を呼び出してしまうということがあります。 (例えば、コールバック用のメソッドとして使用される場合) ここで特別な注意を払わなければ、通常は元のオブジェクトへの参照は失われてしまいます。 バインドされた関数の作成は、元のオブジェクトへの参照を有効にし、この問題を解決してくれます。

this.x = 9;
var module = {
  x: 81,
  getX: function() { return this.x; }
};

module.getX(); // 81

var getX = module.getX;
// "this"はグローバルオブジェクトが参照されるため、
// 下記の結果は9になります。
getX(); // 9

// 'this'をmoduleへバインドして、
// 新しい関数を作成します。
var boundGetX = getX.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(undefined, 37);

var list2 = leadingThirtysevenList(); // [37]
var list3 = leadingThirtysevenList(1, 2, 3); // [37, 1, 2, 3]

setTimeoutでの使用

デフォルトでwindow.setTimeout()は、 this値にwindow(グローバル)オブジェクトを設定します。 クラスのメソッドとして動作させるためthisを、クラスのインスタンスへの参照にする必要がある場合、 インスタンスへの参照を保持するために、 thisをコールバック関数へ明示的にバインドさせることが可能です。

function LateBloomer() {
  this.petalCount = Math.ceil( Math.random() * 12 ) + 1;
}

// 1秒遅らせて、declare
LateBloomer.prototype.bloom = function() {
  window.setTimeout( this.declare.bind( this ), 1000 );
};

LateBloomer.prototype.declare = function() {
  console.log('私は' + this.petalCount + '枚の花びらを持つ美しい花です!');
};

コンストラクタとして、バインドさせた関数を使用

警告: このセクションではJavaScriptの機能をデモするため、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 */);
// 後述するpolyfillでサポートされません。
// ネイティブのbindで有効です。
var YAxisPoint = Point.bind(null,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; // false

newを使用するバインドされた関数を作成するのに、 特別なことをする必要が無いことに注意してください。 例え、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()を外すことが出来る事を意味します。

// 前述した例の"slice"と等価です。
var unboundSlice = Array.prototype.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") {
      // ECMAScript 5の内部IsCallable関数に出来るだけ近づけます。
      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;
  };
}

このアルゴリズムと仕様のアルゴリズムには相違点が多くあり、 その幾つかについては下記を参照してください。 (この一覧は完璧なものを目指したものでは無いため、他にもあるかもしれません)

  • この不完全な実装は、それぞれ本来の値を持つ、下記の組み込みメソッドに依存しています。

  • この不完全な実装が作成する関数は、get、set、削除の際にTypeErrorをスローする、 ポイズンピルの役目を果たす不変のプロパティcallerarguments(訳注:おそらく予期しない使用がされないための)を持ちません。(翻訳に自信なし) (実装でObject.definePropertyがサポートされていれば追加することが可能です。 もしくは、__defineGetter__と__defineSetter__の拡張がサポートされていれば、 不完全な実装(削除時の例外スロー無し)をすることが出来ます。(翻訳に自信なし)
  • この不完全な実装が作成する関数は、prototypeプロパティを持ちます。 (本来のバインドされた関数は、これを持ちません。)
  • この不完全な実装が作成する関数のlengthプロパティは、 ECMA-262によって義務付けられている内容に従ったものではありません。 この不完全な実装がlengthが0の関数を作成する一方、 完全な実装では対象となる関数に依存して予め指定された引数の数になるため、 0以外のlengthを返すことがあります。

この不完全な実装を使用することを選ぶのであれば、 ECMA-262の第5版の挙動から外れている、これらのケースに依存するものにしないでください! それでも注意深く使用することが出来れば(また、仕様に必要な適切な修正を追加することが出来れば)、この不完全な実装でも、 ブラウザ全体に仕様に沿った実装が広まるまでの繋ぎにすることが出来るでしょう。

仕様

ブラウザ互換性

デスクトップ
機能 Chrome Firefox
(Gecko)
IE Opera Safari
基本 7 4.0 (2) 9 11.6 5.1.4
モバイル
機能 Android Chrome for
Android
Firefox
Mobile
IE
Mobile
Opera
Mobile
Safari
Mobile
基本 4.0 0.16 4.0 (2) ? 11.5 6.0

関連項目

 Back to top

© 2017 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.

このページは、ページトップのURL先のMozilla Developer Network(以下、MDN)のコンテンツを翻訳した内容を基に構成されています。 構成について異なる点も含まれますので、下記の項目を確認し、必要に応じて元のコンテンツをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。

  • 特定のブラウザに特化しすぎている情報やあまりにも古い情報、 または試験的に導入されているようなAPIや機能については、省略していることがあります。
  • 例やデモについて、実際にページ内で動作させる関係で一部ソースコードを変更している場合や、 その例で使用しているコンテンツの単語や文章などを日本人向けに変更しいてる場合があります。
  • MDNの更新頻度が高いため、元のコンテンツと比べ情報が古くなっている可能性があります。
  • "訳注:"などの断わりを入れた上で、日本人向けの情報の追記を行っている事があります。