概要
JavaScriptにおける this
の挙動は、実行時の呼び出し方によって決まるという点で、他言語とは一線を画す。
つまり this
は、定義ではなく呼び出しによって決定される。
以下のような状況において、this
の設計がバグの温床になる:
- コールバックで
this
が失われる - メソッドの
this
が意図しないオブジェクトになる -
bind()
を忘れて関数が壊れる - アロー関数と通常関数が混在して挙動が読めない
この記事では、this
の挙動を完全に制御するために、暗黙的・明示的・アロー関数の使い分け戦略を提示する。
thisの決定ルール
① new キーワードが使われている? → thisは新しいインスタンス
② 明示的に bind/call/apply? → thisは指定された値
③ オブジェクトからの呼び出し? → thisはそのオブジェクト
④ アロー関数? → 外側のthisを継承
⑤ 上記いずれもなし → thisはグローバル(strictなら undefined)
1. 暗黙的バインディング(呼び出し元に依存)
const user = {
name: 'Toto',
greet() {
return `Hello, ${this.name}`;
}
};
user.greet(); // Hello, Toto
→ this
は user
にバインドされる
❌ 呼び出し方が変わると this
が消える
const fn = user.greet;
fn(); // ❌ undefined(または global の name)
2. 明示的バインディング:bind / call / apply
function greet() {
return `Hello, ${this.name}`;
}
const user = { name: 'Toto' };
greet.call(user); // Hello, Toto
greet.apply(user); // Hello, Toto
const bound = greet.bind(user);
bound(); // Hello, Toto
✅ 使い分け
メソッド | 引数の渡し方 | 実行タイミング |
---|---|---|
call |
値をカンマで並べて渡す | 即時 |
apply |
引数を配列で渡す | 即時 |
bind |
引数を保存して返す関数 | 遅延(再実行) |
3. アロー関数によるバインディングの固定
const obj = {
name: 'Toto',
greet: () => {
console.log(this.name);
}
};
obj.greet(); // ❌ undefined(thisはグローバル)
// 正しい使い方
class Button {
constructor(label) {
this.label = label;
this.onClick = () => {
console.log(this.label); // ✅ 常にインスタンスのthis
};
}
}
→ アロー関数は this
を定義時に外側から自動的に継承する
this に関する典型的なバグ例
❌ setTimeoutで this が失われる
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++; // ❌ this はグローバル
}, 1000);
}
→ ✅ 解決策:アロー関数 or bind
setInterval(() => this.seconds++, 1000);
設計で選ぶ this バインディング戦略
シーン | 推奨構文 | 理由 |
---|---|---|
コールバックでthis保持 | アロー関数 | スコープ継承で安全 |
メソッド定義 | 通常関数(宣言/式) | 呼び出し元バインド |
再利用可能な関数 | bind | 明示的にバインド |
setTimeout / setInterval での this | アロー関数 or bind | 暗黙バインディングは破綻する |
thisを完全に理解する一問一答
const obj = {
val: 42,
method() {
return this.val;
}
};
const f = obj.method;
f(); // ❌ undefined
obj.method(); // ✅ 42
f.call(obj); // ✅ 42
const b = obj.method.bind(obj);
b(); // ✅ 42
結語
JavaScriptの this
は「文法的に定まるもの」ではなく、「設計的に定義するもの」である。
-
function
か=>
かではなく、“どこで実行されるか”が重要 -
this
を失わない設計をすることは、“コードの一貫性”を保証するということ - 明示(bind)・継承(arrow)・委譲(call/apply)を意図的に選び分けることが重要
「なぜ this をこう扱ったのか」を説明できる設計者が、JavaScriptを制する。