今日は、ES6 で導入されたアロー関数と通常の関数(functionとかくやつ)の違いを紹介します。「アロー関数を使えば、すっきりと関数を記述できる!」というのは事実ですが、オブジェクトのメソッド内でのthisの取り扱いに注意する必要があるので、その辺りをまとめます。
Contents
アロー関数とは
まずはアロー関数の定義をさらっと確認しておきましょう。
- より短く記述できる通常の関数式の代替構文
- this、 super、 new.target を束縛しない
- アロー関数式はメソッドでない関数に最適
1は、そのまんまですが、2、3についてはこれから紹介する例を見れば、その意味することがわかりやすいかと思います。
オブジェクトのメソッド内での”this”取り扱い時の注意(うまくいかない例)
まずは、アロー関数なしで、thisの取り扱いに注意しなければならない例を紹介します。
ポイントは「メソッドの呼び出し元のオブジェクトが何であるかを確認する」ことです。callAgeメソッドの、setTimeout関数に注目して下さい。
var profile = {
name: "Keisuke",
age: 33,
callAge: function() {
// 実行時の呼び出し元オブジェクトはprofileなのでthis.ageにアクセス可能;
console.log(this.age); // 33
setTimeout(function() {
// setTimeout() 実行時の呼び出し元オブジェクトはグローバルオブジェクト
console.log("歳は" + this.age); // 歳はundefined
}, 1000);
},
callName: function() {
// 実行時の呼び出し元オブジェクトはprofileなのでthis.nameにアクセス可能;
console.log("私の名前は" + this.name); // 私の名前はKeisuke
}
};
profile.callAge();
profile.callName();
この例では、setTimeout関数にthisをbindしていないので ”歳はundefined” と出力されています。なぜそうなるかは、以下のMDNの解説の通りです。
setTimeout()
によって実行されるコードは、setTimeout
が呼び出された関数とは別の実行コンテキスト内から呼び出されます。呼び出された関数にthis
キーワードを設定する通常の規則を適用して、呼び出しあるいはbind
でthis
を設定しなければ、非 strict モードではglobal
(またはwindow
)、strict モードでは undefined になります。これは、setTimeout
が呼び出された関数のthis
値と同じにはなりません。
ES6前までは、このような時には、次の例のようにbind関数を指定していました。
アロー関数の導入以前は、以下のように”this”にアクセス
【方法1】”this” をbindする
setTimeout関数にcallAgeメソッドのthis、つまり profile を bind してあげると、うまくいきます。
var profile = {
name: "Keisuke",
age: 33,
callAge: function() {
setTimeout(
function() {
// callAgeメソッドのthis、つまり profile を bind してあげる
console.log("歳は" + this.age); // 歳は33
}.bind(this),
1000
);
},
callName: function() {
console.log("私の名前は" + this.name); // 私の名前はKeisuke
}
};
profile.callAge();
profile.callName();
【方法2】”this”の値をアクセス可能な別の値に割り当てる
こちらもシンプル。setTimeoutよりも一つ上のスコープ内なら、thisにアクセス可能なので、そのスコープでthis
をthat
に割り当て、that.ageという形で、ageにアクセス。
var profile = {
name: "Keisuke",
age: 33,
callAge: function() {
// アクセス可能なスコープ内で、thisをthatに割り当てる
var that = this;
setTimeout(function() {
// thisではなくて、thatからageの値にアクセス
console.log("歳は" + that.age); // 歳は33
}, 1000);
},
callName: function() {
console.log("私の名前は" + this.name); // 私の名前はKeisuke
}
};
profile.callAge();
profile.callName();
setTimeoutに、アロー関数を使用します
ここでアロー関数の出番です。setTimeoutに通常の関数ではなく、代わりにアロー関数を使用します。アロー関数はthisを束縛しない(アロー関数自身はthisを持たない)ので、setTimeout関数を取り囲むcallAge関数のthis、つまりprofileが入ります。
var profile = {
name: "Keisuke",
age: 33,
callAge: function() {
setTimeout(() => {
// アロー関数を使うことにより、callAgeメソッドの”this”、つまり”this”はちゃんとprofileオブジェクトを参照する
console.log("歳は" + this.age); // 歳は33
}, 1000);
},
callName: function() {
console.log("私の名前は" + this.name); // Keisuke
}
};
profile.callAge();
profile.callName();
注意!callNameにもアロー関数を使ってみると上手くいきません
ここで、「callNameメソッドにもアロー関数を使えば、すっきりと記述できていいじゃん!」と思うかもしれませんが、それはうまくいきません。
var profile = {
name: "Keisuke",
age: 33,
callAge: function() {
setTimeout(() => {
console.log("歳は" + this.age); // 歳は33
}, 1000);
},
callName: () => {
// アロー関数は自身で"this"を持たないので, this.name は undefinedとなる。
// これが、アロー関数式はメソッドでない関数に最適であると言われる理由の一つ。
console.log("私の名前は" + this.name); // 私の名前は undefined
}
};
profile.callAge();
profile.callName();
まとめ:アロー関数式と通常の関数の違い
最後にもう一度、アロー関数の定義をみておきましょう。
- より短く記述できる通常の関数式の代替構文
- this、 super、 new.target を束縛しない
- アロー関数式はメソッドでない関数に最適
この記事では、super、 new.targetには触れていませんが、前述の例のように、2と3は関連しています。アロー関数はメソッドには向かない理由、通常の関数式と違いthisを束縛しないという特徴を覚えておきましょう!