アロー関数で this を操る!
はじめに
JavaScript の this
、混乱したことありませんか?
「呼び出し方で変わる」「イベントで指すものが違う」──誰もが一度はハマるポイントです。
でも、Arrow Function(アロー関数)を使うと this
の挙動が一気にわかりやすくなります。
この記事では、「なぜ Arrow Function では this
が変わらないのか?」を
ES5 の関数型の書き方で解説します。
※
class
構文は使いません。全部関数とオブジェクトリテラルでいきます!
Arrow Function はただの省略記法じゃない
const greet = (name) => {
console.log(`Hello, ${name}!`);
};
見た目は短く書けるだけに見えますが、
実は this の仕組みがまったく違う のがポイントです。
Arrow Function は自分自身の this
を持たず、
定義されたときのスコープ(外側)にある this
をそのまま使うようになっています。
this
は「呼び出し時」ではなく「定義時」に決まる
普通の関数は、呼び出し方によって this
が変わります。
たとえばオブジェクトから呼ぶとそのオブジェクトが、
単独で呼ぶと window
(または undefined
)になります。
でも Arrow Function は違います。
定義されたときにすでに this
が固定されるんです(=レキシカルスコープ)。
実例①:オブジェクト内の this が取れない!?
const user = {
name: "Alice",
greet: () => {
console.log(`Hello, ${this.name}`);
},
};
user.greet(); // Hello, undefined
このコード、user.greet()
と呼んでいるのに this.name
が取れません。
なぜかというと、greet
は Arrow Function なので
外側(グローバル)の this
を参照してしまうからです。
💡 オブジェクトのメソッドに Arrow Function を使うのは NG!
this
はそのオブジェクトを指しません。
実例②:コールバック関数では救世主になる
では、Arrow Function が"便利に働く"ケースを見てみましょう。
const counter = {
count: 0,
start: function () {
setInterval(() => {
this.count++;
console.log(this.count);
}, 1000);
},
};
counter.start();
setInterval
の中で Arrow Function を使っています。
普通の関数だと this
は window
を指してしまいますが、
Arrow Function は外側(ここでは start
メソッド)の this
を引き継ぎます。
つまり、this.count
は正しく counter.count
になります。
実例③:関数コンストラクタでも this が保たれる
ES5 風にコンストラクタ関数を書いた場合も、
Arrow Function のレキシカル this はとても役立ちます。
function Timer() {
this.seconds = 0;
setInterval(() => {
this.seconds++;
console.log(this.seconds);
}, 1000);
}
const t = new Timer();
setInterval
の中の this
は、ちゃんと Timer
インスタンスを指します。
これを普通の関数で書くと、this
が window
に変わってしまいます。
昔は bind(this) で頑張っていた
ES5 の頃は、コールバック内で this
を保つために
よく .bind(this)
や var self = this;
を使っていました。
function Timer() {
this.seconds = 0;
var self = this;
setInterval(function () {
self.seconds++;
console.log(self.seconds);
}, 1000);
}
あるいはこう。
function Timer() {
this.seconds = 0;
setInterval(function () {
this.seconds++;
}.bind(this), 1000);
}
でも、Arrow Function が登場してからはこの面倒な手間が不要になりました。
自然に外側の this をキャプチャしてくれるんです。
"this を閉じ込める"という感覚
Arrow Function は「自分の this を持たず、外側の this
を閉じ込める(キャプチャする)」仕組みです。
そのため、コールバック関数や非同期処理でも this がブレません。
function App() {
this.message = "Hello from App!";
document.body.addEventListener("click", () => {
console.log(this.message);
});
}
const app = new App();
// クリックすると "Hello from App!"
イベントハンドラの中でも、this は常に App
インスタンスを指します。
これが Arrow Function の大きな強みです。
注意: オブジェクトのメソッドには向かない
オブジェクトのメソッドには Arrow Function を使わないほうがいいです。
const obj = {
value: 123,
show: () => {
console.log(this.value);
},
};
obj.show(); // undefined
Arrow Function の this は「外側」から取ってくるため、
オブジェクトそのものを指すことができません。
→ メソッドは普通の function
記法(または短縮メソッド構文)で定義するのがベターです。
まとめ
Arrow Function の最大の特徴は「this を定義時に固定する」こと。
これにより、\
- コールバック内で this が変わらない\
- bind や self = this が不要になる\
- 非同期処理やイベントリスナーで扱いやすい
ただし、\
- オブジェクトメソッドには不向き(this が外に逃げる)
という点に注意すれば、Arrow Function は非常に強力なツールです。
Happy Lexical Scoping! ☕️
「this が変わらない」だけで、JavaScript がぐっと楽になります。
Discussion