概要
this
は JavaScript最大の罠とも言われる。
なぜならその挙動は、“関数の中身”ではなく、“関数の呼ばれ方”で決まるからだ。
this
の正体を見極めるには、呼び出しコンテキストとバインディングのルールを体系的に理解する必要がある。
この記事では、JavaScriptにおける this
の動的バインディングのルールと、それに対応するコード設計を構造的に解説する。
対象環境
ES5〜ES2023対応のJavaScript環境(Node.js / ブラウザ)
this
の正体:“呼び出し元のコンテキスト”を参照する特殊キーワード
- 実行時に動的に決定される
- 関数の宣言時ではなく、呼び出し時に決まる
-
this
の中身は、「誰が呼び出したか」によって変化する
呼び出しパターン別 this の挙動一覧
呼び出し方 |
this の参照先 |
---|---|
通常の関数呼び出し(非 strict) | グローバルオブジェクト(window / global ) |
通常の関数呼び出し(strictモード) | undefined |
メソッド呼び出し | メソッドの所有オブジェクト |
コンストラクタ(new )呼び出し |
新しく生成されたインスタンスオブジェクト |
明示的バインド(call / apply ) |
第一引数で指定したオブジェクト |
永続バインド(bind ) |
bind() で指定されたオブジェクト |
イベントリスナー(DOM) | イベント発火元のDOM要素 |
アロー関数 | 外側スコープの this を継承 |
クラスメソッド(通常) | インスタンス |
クラスの静的メソッド | クラス自身 |
各パターンを実例で確認
✅ 通常関数(非strict)
function show() {
console.log(this); // グローバル(window / global)
}
show();
✅ strictモード下
'use strict';
function show() {
console.log(this); // undefined
}
show();
✅ メソッドとして呼び出す
const obj = {
name: 'Alice',
greet() {
console.log(this.name); // 'Alice'
}
};
obj.greet();
✅ アロー関数
const obj = {
name: 'Alice',
greet: () => {
console.log(this.name); // undefined(外側の this → グローバル)
}
};
obj.greet();
→ アロー関数は this
を自動的に閉じ込める(レキシカルスコープ)
✅ イベントリスナー内の this
button.addEventListener('click', function () {
console.log(this); // クリックされたボタン要素
});
→ ただし、アロー関数で書くと this が変わる:
button.addEventListener('click', () => {
console.log(this); // 外側の this(=window or undefined)
});
✅ call
, apply
, bind
による明示的指定
function show() {
console.log(this.name);
}
const user = { name: 'toto' };
show.call(user); // 'toto'
show.apply(user); // 'toto'
const bound = show.bind(user);
bound(); // 'toto'
✅ コンストラクタでの this
function User(name) {
this.name = name;
}
const u = new User('toto');
console.log(u.name); // 'toto'
✅ クラスメソッドでの this
class Person {
constructor(name) {
this.name = name;
}
greet() {
console.log(this.name);
}
}
const p = new Person('toto');
p.greet(); // 'toto'
よくあるバグパターンと対策
❌ this が undefined / 意図外になる
const obj = {
count: 0,
increment() {
setTimeout(function () {
this.count++;
}, 100);
}
};
→ ✅ 対策:アロー関数 or bind(this)
setTimeout(() => {
this.count++; // 外側の this を使う
}, 100);
this 設計の原則
シーン | 推奨する this 設計 |
---|---|
コールバック関数 | アロー関数でスコープを固定 |
コンストラクタ / クラス | 通常の関数(this を使う) |
状態を閉じ込めたい関数 | アロー関数を使わずクロージャで管理 |
イベントリスナー | function + bind(this) or クラスで管理 |
ライブラリ間の橋渡し |
call / apply で明示的に指定 |
結語
this
は魔法ではない。
それは、「どこから呼ばれたのか」という文脈(コンテキスト)そのものだ。
thisを理解するとは、「関数が、誰に責任を持って処理を行っているのか」を理解することでもある。
関数の本質は処理ではない。責任の構造である。
すべての this を意図して使う。
それこそが、JavaScriptを“制御できる”状態の第一歩だ。