thisバインディングの決定規則(4つの束縛と優先順位)
this が何を指すか迷わなくなる。既定・暗黙・明示・new の4束縛とアロー関数のレキシカル参照を、呼び出し時にどう決まるか優先順位つきで内部から整理します。
- 1.this は定義時ではなく呼び出し時に決まる。判定の優先順位は new > 明示(bind/call/apply) > 暗黙(obj.method()) > 既定(素の呼び出し)の順で一意に決まる。
- 2.アロー関数だけは別格で、自分の this を持たず外側のスコープから静的に受け継ぐ。call/apply/bind や new でも書き換わらない。
- 3.暗黙束縛は呼び出しの“ドットの左”が決める。メソッドを変数に代入したりコールバックに渡すと参照が外れ、strictでundefined・非strictでglobalThisになる。
this は「呼び出し方」で決まる
多くの言語で this(あるいは self)はクラスのインスタンスに固定されますが、JavaScript の this は 関数がどう呼ばれたか で毎回決まります。同じ関数オブジェクトでも、呼び出しのされ方が違えば this は別物を指します。鍵は 定義時ではなく呼び出し時に評価される こと。これを押さえないと、メソッドをコールバックに渡した瞬間に this が外れる典型バグを読み解けません。基礎は JavaScript と DOM を前提にします。
通常関数の this を決める規則は4つしかありません。既定束縛・暗黙束縛・明示束縛・new束縛 です。そしてアロー関数だけはこの枠組みの外にいて、独自の レキシカル this を持ちます。本記事は、この4束縛の中身と、複数が競合したときの 優先順位 を内部から整理します。
4つの束縛
それぞれ「呼び出し式の形」がトリガーになります。
- 既定束縛(default):
f()のように、ドットもnewもcall系もない素の呼び出し。strict モードではthisはundefined、非strict(sloppy)ではglobalThis(ブラウザならwindow)になる。 - 暗黙束縛(implicit):
obj.method()のように ドットの左にオブジェクトがある 呼び出し。thisはそのドット直前のオブジェクト(ここではobj)になる。a.b.c()ならチェーン最後のbがthis。 - 明示束縛(explicit):
f.call(ctx)/f.apply(ctx)/f.bind(ctx)でthisを 明示的に指定 する。call/applyは即時呼び出し、bindはthisを固定した新しい関数を返す。 - new束縛(new):
new F()。新しい空オブジェクトを生成し、それをthisとしてFを実行し、Fが明示的にオブジェクトを返さなければその新オブジェクトを返す。
function who() { return this && this.name; }
const o = { name: 'o', who };
who(); // 既定束縛 → strict:undefined / 非strict:globalThis
o.who(); // 暗黙束縛 → o
who.call({ name: 'x' }); // 明示束縛 → {name:'x'}
new who(); // new束縛 → 新規オブジェクト(name未設定)
暗黙束縛で this になるのは、呼び出し式の 最後のドットの直前 にある値だけです。a.b.c() の this は a ではなく b です。逆にドットを使わず関数参照だけを取り出して呼ぶ(const f = o.who; f())と、ドットが消えるので暗黙束縛は成立せず、既定束縛に落ちます。これが「メソッドを変数に入れたら this が消えた」の正体です。
優先順位:競合したらどれが勝つか
4束縛は同時に当てはまることがあります。そのときの強さは仕様上 一意 で、強い順に並べると次のとおりです。
| 優先度 | 束縛 | トリガー | this になる値 |
|---|---|---|---|
| 1(最強) | new束縛 | new F() | 生成された新しいオブジェクト |
| 2 | 明示束縛 | f.call / f.apply / f.bind | 指定した ctx(ただし new は例外) |
| 3 | 暗黙束縛 | obj.method() | ドット直前の obj |
| 4(最弱) | 既定束縛 | 素の f() | strict:undefined / 非strict:globalThis |
判定は「上から順に、当てはまる最初のものを採用」と考えると正確です。たとえば bind 済みの関数を obj.method() の形で呼んでも、明示束縛(優先度2)が暗黙束縛(優先度3)に勝つので、this は bind で固定した側のままです。
function who() { return this.name; }
const bound = who.bind({ name: 'bound' });
const o = { name: 'o', m: bound };
o.m(); // 'bound' … 明示(2) > 暗黙(3)
bind で固定した関数を new した場合、new束縛(優先度1)が明示束縛(優先度2)に勝ち、bind で渡した this は無視されて新規オブジェクトが this になります。仕様上、bind の生成する束縛関数は内部で「new 経由かどうか」を判定し、new のときは固定した this を捨てます。一方 bind の 引数(部分適用) は new でも残ります。「this は new が上書き、引数は残る」と覚えると正確です。
アロー関数は4束縛の外にいる
アロー関数は 自分自身の this を持ちません。call/apply/bind で渡しても、obj.method() で呼んでも、this は変わらない。アロー関数は定義された時点の 外側のレキシカルスコープの this を静的に捕捉し、それを使い続けます。arguments・super・new.target も同様に外側から受け継ぎ、コンストラクタにもできません(new するとエラー)。
const obj = {
name: 'obj',
normal() { return (() => this.name)(); }, // 内側アローは normal の this を継承
};
obj.normal(); // 'obj'(normal が暗黙束縛で obj → アローが継承)
const f = () => this; // 定義位置の this を捕捉
f.call({ name: 'x' }); // 渡しても無視 → 捕捉済みの this のまま
setTimeout(obj.method, 0) のようにメソッド参照を渡すとドットが外れ、this が既定束縛に落ちます。対策の定番は ❶ obj.method.bind(obj) で明示束縛する、❷ () => obj.method() とアローで包んで暗黙束縛を保つ、のいずれか。クラスのイベントハンドラを handle = () => {…} とクラスフィールドのアローで書くのも、インスタンスの this を恒久的に捕捉する常套手段です。
評価の流れを内部から
呼び出し式 expr(args) に出会ったとき、エンジンは概ね次の順で this を決めます。地の文では集合を {new, explicit, implicit, default} のようにインラインコードで書きます。
1. expr を評価して「呼ばれる関数」と「参照のベース」を得る
- obj.method の形なら base = obj(暗黙束縛の候補)
- 単なる識別子なら base なし
2. 関数がアローなら → 捕捉済みの [[ThisValue]] を使い、以降は無視して終了
3. new 経由なら → 新オブジェクトを生成し this に(最優先)
4. bind 済みなら → 固定した this を使う(ただし new には負ける)
5. base があれば → base を this に(暗黙束縛)
6. どれでもなければ → strict:undefined / 非strict:globalThis(既定束縛)
ここで効くのが 参照(Reference) という内部概念です。obj.method という式は、評価の途中では「値(関数)+ベース(obj)」をひとまとめにした参照として扱われ、() を付けて呼ぶ瞬間にベースが this へ供給されます。const f = obj.method のように 一度変数へ代入すると参照がただの関数値に縮退し、ベース情報が失われる ――だから暗黙束縛が消えるのです。this の決定は実行時の最適化対象でもあり、形が安定するほど速くなる事情は JavaScriptエンジンの内部 に通じます。
既定束縛の this は、関数本体が strict モードかどうかで分岐します。strict では undefined、非strictでは globalThis に 暗黙変換 されます。ES モジュールと class 本体は常に strict なので、その中の素の関数呼び出しは this === undefined。this.name に触れると TypeError(undefined のプロパティ参照)になります。非strict の globalThis への自動フォールバックは、グローバルを汚染する事故の温床なので、strict を前提に書くのが安全です。
まとめ
通常関数の this は 呼び出し時 に、new > 明示(bind/call/apply) > 暗黙(obj.method) > 既定(素の呼び出し) の優先順位で一意に決まります。暗黙束縛は「ドットの左」が供給するため、メソッドを変数やコールバックに渡すと参照が縮退して外れ、strict なら undefined、非strict なら globalThis に落ちます。アロー関数だけは別格で、自分の this を持たず外側スコープから静的に捕捉するので、call/apply/bind や new でも変わりません。コールバックの this 外れは bind かアローで防ぐ――この一連の規則を、非同期の実行順を扱う イベントループの内部構造 と合わせて押さえると、ハンドラ内の this の挙動まで筋道立てて読めます。
Web/フロントエンド Article
thisバインディングの決定規則(4つの束縛と優先順位)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
JavaScript
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
アロー関数だけは別格で、自分の this を持たず外側のスコープから静的に受け継ぐ。call/apply/bind や new でも書き換わらない。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「JavaScript / this」に近いか確認する。
- 強みである「this は定義時ではなく呼び出し時に決まる。判定の優先順位は new > 明示(bind/call/apply) > 暗黙(obj.method()) > 既定(素の呼び出し)の順で一意に決まる。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。