nayucolony

勉強したこととか

インスタンス内において、アロー関数の「this」はインスタンスを参照しない

Vue.jsに限った話ではないが、Vue.jsを触っていて湧いたちょっとした疑問を調べたメモ。

TL;DR

  • アロー関数内のthisは「Vueインスタンスを参照したい」という文脈での使用に適さないのでつかわない。
  • functionによる関数宣言では「インスタンス内のthisインスタンスそのもの」なのでVueインスタンスを参照できる。
  • アロー関数では「呼ばれた場所がthis」。
  • 例えばcomputedのメソッド内のthisは「computedのオブジェクト」であり、インスタンスではないので不適。

経緯

Vue.jsに書いてある

算出プロパティ(例 aDouble: () => this.a * 2) を定義するためにアロー関数を使用すべきではないことに注意してください。アロー関数は、this が期待する Vue インスタンスではなく、this.a が undefined になるため、親コンテキストに束縛できないことが理由です。

とか

data プロパティ(例 data: () => { return { a: this.myProp }}) でアロー関数を使用すべきではないことに注意してください。アロー関数は、this が期待する Vue インスタンスではなく、this.myProp が undefined になるため、親コンテキストに束縛できないことが理由です。

これが気になってちゃんと調べた。

解説

Vue.jsを使用する際、「Vueインスタンスdataオプションのオブジェクトに入ってる値」を参照して計算することが多々ある。 例えばこんな感じ(Vue.jsより)

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // get のみ。必要なのは関数一つだけ
    aDouble: function () {
      return this.a * 2
    },
    // get と set 両方
    aPlus: {
      get: function () {
        return this.a + 1
      },
      set: function (v) {
        this.a = v - 1
      }
    }
  }
})
vm.aPlus   // -> 2
vm.aPlus = 3
vm.a       // -> 2
vm.aDouble // -> 4

このとき、this.aは「dataオプションのオブジェクトのプロパティ名a」をさしている。

これは、「コンストラクタを呼び出してインスタンスを生成したとき、インスタンス中のthisインスタンスそのものをさす」ことによる。 datamethodなどのオプションのオブジェクトのプロパティは、Vueインスタンス直下に格納されるので、this.aのようにして参照できる。

ちなみに、所属するオプションを明示的にしてアクセスをする場合、this._data.aだったりthis.$data.aだったりでアクセスできる。 (ただし、アクセスできるものの、「完全に代替できる」とはいえないっぽいということがオプション / データ - Vue.jsに書いてある。 使用する際は一読することを推奨する。)

では、アロー関数でのthisはどうなのか。 アロー関数のthisは、functionの時のように「インスタンス内ではそのインスタンスをさす」という動きはしない。

以下は先ほどの例をただアロー関数に変えたもの。

var vm = new Vue({
  data: { a: 1 },
  computed: {
    // get のみ。必要なのは関数一つだけ
    aDouble: () => {
      return this.a * 2
    },
    // get と set 両方
    aPlus: {
      get: () => {
        return this.a + 1
      },
      set: (v) => {
        this.a = v - 1
      }
    }
  }
})

ここで、一部分に着目

var vm = new Vue({
  data: { a: 1 },
  computed: {

    aDouble: () => {
      return this.a * 2
    },

  }
})

このとき、funcitonではthis.adataオプションのaを参照できていた。

しかし、アロー関数を用いるとそうはできない。なぜなら、アロー関数は「呼び出された場所をthisとする」という動きをするからだ。 「呼び出された場所」は今回のケースだと、「computedオプションのオブジェクト」にあたる。 そのオブジェクトの中にaをプロパティ名として持つ要素は存在しないので、undefinedとなる。

これが、

アロー関数は、this が期待する Vue インスタンスではなく

という文章の意味だった。たしかにVueインスタンスを参照できていないので不適だ。 this使ってない場合はアロー関数でも問題はないが、混合させるくらいならfunctionで統一すべきだろう。

関連

アロー関数がどうのこうのと言う前に、そもそもthisは結構文脈によってさすものが違うので注意。 JavaScriptの「this」は「4種類」?? - Qiita