🧙

アロー関数で 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 を使っています。

普通の関数だと thiswindow を指してしまいますが、
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
インスタンスを指します。
これを普通の関数で書くと、thiswindow に変わってしまいます。


昔は 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