6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptにおける無名関数とアロー関数の「this」のスコープの違いについて

Posted at

前置き

これについて学ぶ発端となったのは、以下のコードを書いているときでした。
リクエストを並列に走らせたく、Promise.allを使う必要がありました。
Promise.allの引数には、配列でPromiseオブジェクトを渡す必要があるので、下のputs()というPromiseオブジェクトを返す関数も作成しました。

以下ではエラーが出るのですが、どこが間違いか気づきますか?

method(A, B) {
  // 処理
}

puts(A, B): Promise<void> {
    return new Promise(function(resolve) => {
      this.$api
        .get('/tech-talk')
        .then(response => {
           this.method(A, B)
        })
        .then(() => resolve())
    })
 }

funcA(): Promise<void> {
    const promises = [
      this.puts(1, 1),
      this.puts(1, 2),
      this.puts(2, 1),
      this.puts(2, 2)
    ]
    Promise.all(promises).then(function() {
      this.funcB()
    })
}

間違いは以下になります。

puts(A, B): Promise<void> {
    return new Promise(function(resolve) => {
      // 'this' implicitly has type 'any' because it does not have a type annotation
      this.$api
        .get('/tech-talk')
        .then(response => {
           // 'this' implicitly has type 'any' because it does not have a type annotation
           this.method(A, B)
        })
        .then(() => resolve())
    })
 }

functionA(): Promise<void> {
    const promises = [
      this.puts(1, 1),
      this.puts(1, 2),
      this.puts(2, 1),
      this.puts(2, 2)
    ]
    Promise.all(promises).then(function() {
      // 'this' implicitly has type 'any' because it does not have a type annotation
      this.functionB()
    })
}

エラーメッセージが出ているのは、全て「this」を扱っている箇所です。
最初なぜなのか分かりませんでした。

正解パターンは以下です。

puts(A, B): Promise<void> {
    return new Promise(resolve => {
      this.$api
        .get('/tech-talk')
        .then(response => {
           this.method(A, B)
        })
        .then(() => resolve())
    })
 }

functionA(): Promise<void> {
    const promises = [
      this.puts(1, 1),
      this.puts(1, 2),
      this.puts(2, 1),
      this.puts(2, 2)
    ]
    Promise.all(promises).then(() => this.functionB())
}

Promiseのコールバック関数を無名関数からアロー関数に変更しただけです。

無名関数とアロー関数のthisの取り扱い方の違い

関数 thisの扱い方
無名関数 それが何を指し示すかは文脈によって変化する。
そのメソッドにアクセスしたオブジェクトが、thisとなる。
アロー関数 文脈に依存せず、宣言された時点でthisを確定する。
呼び出された場所をthisとする

上に掲載したコードを例にすると、コールバック関数が無名関数の場合、thisはPromiseオブジェクト自身にあたるので、外のdataやmethodを参照できない。

逆にアロー関数の場合、呼び出し元からthisのスコープが引き継がれるので、外のdataやmethodも参照ができる。

補足
function が基準スコープになるのが JavaScript の大きな特徴 (var のスコープもこれに準ずる)。
そのため、function を入れ子にした途端 this の指すものが変わってきてしまうので注意が必要。
それに対して、アロー関数を利用すると関数をどんなに入れ子にしてもthisの指すものは変わらず固定化される。

その他無名関数とアロー関数の違い

JavaScript: 通常の関数とアロー関数の違いは「書き方だけ」ではない。異なる性質が10個ほどある。

参考記事

【Vue.js】Promiseのcallback処理にComponentで定義したmethodの使用
JavaScript の this を理解する多分一番分かりやすい説明

6
2
0

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
6
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?