36
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptAdvent Calendar 2024

Day 2

JavaScriptのthisもこれで完璧!呼び出し方で変わる5つのパターン

Last updated at Posted at 2024-11-09

こんにちは、とまだです。

JavaScript アドベントカレンダー 2024 のうち、2 日目の記事をお届けします!

私が本格的に現場で JavaScript をはじめたとき、「this を理解したら中級者」と言われました。

それぐらい、this は JavaScript において重要な概念です。

突然ですが、以下のコードの出力結果は分かりますか?

const user = {
  name: "Alice",
  greet() {
    console.log(`こんにちは、${this.name}さん!`);
  },
};

const greet = user.greet;
greet(); // ???

どうでしょうか?
全体として、流れとしては user.greet を呼び出しているので、以下のように出力されると思われるかもしれません。

こんにちは、Aliceさん!

しかし、実際の出力結果は以下の通りです。

こんにちは、undefinedさん!

この結果に驚いた方も多いのではないでしょうか?

実は this は、どう呼び出すかによって値が変わる曲者なんです。

今回は「JavaScript の this を完全に理解する」ことを目指して、5 つのパターンを解説します!

なぜ this を理解する必要があるの?

JavaScript 経験者なら、このような経験があるかもしれません。

  • コードは間違ってないはずなのに undefined が出る
  • React で this.setState が動かない
  • イベントハンドラの中で thisundefined になる

これらの問題は、全て this の理解が足りないことが多いです。

「this がよく分からない」と感じている方は、ぜひこの記事を読み進めてください!

this の基本:「誰が呼び出したか」が重要

まず、this は「関数を呼び出したもの」を指すと覚えておきましょう。

例えば、以下のようなコードを見てみましょう。

const user = {
  name: "Alice",
  greet() {
    console.log(`こんにちは、${this.name}さん!`);
  },
};

// userがgreetを呼び出す
user.greet(); // こんにちは、Aliceさん!

// グローバルスコープでgreetを呼び出す
const greet = user.greet;
greet(); // こんにちは、undefinedさん!

同じ関数なのに、呼び出し方によって結果が変わってしまいます。

なぜこうなるのか、5 つのパターンを見ながら理解していきましょう。

this の 5 つの顔

1. メソッド呼び出し:オブジェクトのメソッドとして呼ぶ場合

オブジェクトのメソッドとして呼び出す場合、this はそのオブジェクトを指します。

const user = {
  name: "Alice",
  greet() {
    console.log(`こんにちは、${this.name}さん!`);
  },
};

user.greet(); // こんにちは、Aliceさん!

この場合、関数を呼び出したのは user オブジェクトなので、thisuser を指します。

2. 普通の関数呼び出し:単独で呼び出す場合

普通の関数として呼び出す場合、thisundefined(strict モード)または window(非 strict モード)を指します。

function standalone() {
  console.log(this);
}

standalone(); // undefined または window

これは意図しない動作の原因になりやすいので、気をつけましょう。

3. アロー関数:外側の this をそのまま使う

アロー関数の this は、関数が定義された場所の this を引き継ぎます。

もう少し正確に言うと、アロー関数は this を持たず、外側の this をそのまま使います。

const user = {
  name: "Alice",
  // 通常の関数
  greet() {
    const arrow = () => {
      console.log(`こんにちは、${this.name}さん!`);
    };
    arrow();
  },
};

user.greet(); // こんにちは、Aliceさん!

アロー関数は this を持たず、外側の this をそのまま使うので、コールバック関数でよく使われます。

4. new による呼び出し:新しいオブジェクトを指す

new で関数を呼び出すと、this は新しく作られたオブジェクトを指します。

function User(name) {
  this.name = name;
}

const alice = new User("Alice");
console.log(alice.name); // Alice

これは、JavaScript のクラス構文の内部でも使われています。

5. bind/call/apply による呼び出し:this を固定する

bindcallapply メソッドを使うと、this の値を明示的に指定できます。

const user = {
  name: "Alice",
  greet() {
    console.log(`こんにちは、${this.name}さん!`);
  },
};

const greet = user.greet.bind(user);
greet(); // こんにちは、Aliceさん!

この方法は、イベントハンドラなどで this の値を固定したい場合によく使われます。

実践的な使い方:よくあるバグと解決策

イベントハンドラでの問題

最もよく遭遇する this のバグは、イベントハンドラでの問題です。

const button = document.querySelector("button");

const user = {
  name: "Alice",
  handleClick() {
    console.log(`${this.name}がクリックしました!`);
  },
};

// 🙅‍♂️ ダメな例:thisがundefinedになる
button.addEventListener("click", user.handleClick);

// 🙆‍♂️ 良い例1:bindを使う
button.addEventListener("click", user.handleClick.bind(user));

// 🙆‍♂️ 良い例2:アロー関数でラップする
button.addEventListener("click", () => user.handleClick());

イベントハンドラ内で this を使いたい場合は、bind やアロー関数を使って this の値を固定しましょう。

React での this の問題

React でクラスコンポーネントを使う場合も、this の問題に遭遇することがあります。

class Button extends React.Component {
  // 🙅‍♂️ ダメな例:thisがundefinedになる
  handleClick() {
    this.setState({ clicked: true });
  }

  // 🙆‍♂️ 良い例1:メソッドをアロー関数で定義
  handleClick = () => {
    this.setState({ clicked: true });
  };

  render() {
    // 🙆‍♂️ 良い例2:bindを使う
    return <button onClick={this.handleClick.bind(this)}>Click me!</button>;
  }
}

ここでも、bind やアロー関数を使って this の値を固定することで問題を解決できます。

最近の React では関数コンポーネントと Hooks を使うことが推奨されており、その場合は this の問題を気にする必要はありません。

まとめ

今回説明した内容を整理すると、以下のようになります。

  1. this は「関数を呼び出したもの」を指す
  2. 呼び出し方によって this の値が変わる
  3. 5 つのパターンを覚えておく
    • メソッド呼び出し
    • 普通の関数呼び出し
    • アロー関数
    • new による呼び出し
    • bind/call/apply による呼び出し

覚えるのが大変そうに見えますが、基本的な考え方さえ理解できれば、あとは実践の中で自然と身についていきます。

最後まで読んでいただき、ありがとうございました!

他にもアドベントカレンダー記事を書いています!

他にも、2024 年のアドベントカレンダーに参加しています。

以下の記事でまとめているので、よければ他の記事も読んでいただけると嬉しいです!

36
39
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
36
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?