上手く説明できるかどうか分かりませんが、function
の方が古くからある記法で、function
内で定義したthis
は、呼び出し時に(誰がその関数をcallしたかによって)this
の中身が変わります。例えばcall
やapply
を使用して、this
の中身を後から変えることもできます。
var obj1 = {i:1, f:function(){return this.i;}};
console.log(obj1.f()); // obj1が呼び出しているので、thisにはobj1が入っています
// 1
window.i = 50
obj1.f.apply(window); // applyを使用してwindowのスコープでfを実行すると、thisはwindowに
// 50
この性質で混乱するのが、例えばsetTimeout
を呼んだ処理です。オブジェクト内で作成したSetTimeout
の引数である無名関数は、トップレベルでのcallになるのでwindowスコープとなり、期待とは違いthis.count
はwindow.count
を指すことになります。
var obj = {
count : 10,
doSomethingLater : function (){
setTimeout(function(){ // この関数のスコープはwindow
this.count++;
console.log(this.count);
}, 300);
}
}
obj.doSomethingLater(); // コンソールに "NaN" と表示。 "count" プロパティは window スコープではないため。
この問題に対して、従来は以下のようにbind
などでthis
となるスコープを拘束する必要がありました。
var obj = {
count : 10,
doSomethingLater : function (){
setTimeout(function(){
this.count++;
console.log(this.count); // 無名関数のスコープを、doSomethingLaterのthisで拘束!
}.bind(this), 300);
}
}
obj.doSomethingLater();
// 約0.3秒後に11と表示
一方、アロー関数では作成時に(?ちょっとこの表現自信ないです)this
が決まります。これを使えば先ほどのsetTimeout
の例はシンプルにこう書けます。
var obj = {
count : 10,
doSomethingLater : function (){
setTimeout(() => {
this.count++;
console.log(this.count);
}, 300);
}
}
obj.doSomethingLater();
// 約0.3秒後に11と表示
アロー関数の方が直感的なので使いやすいですが、例えばインスタンスメソッドには使えないなどいくつかの制約があります。
また、アロー関数自体をサポートしていないブラウザも僅かにあるのでご参考まで。アロー関数で書いたコードは、bind
やapply
などを使ってアロー関数無しでも実現できるので、多くの場合開発時はアロー関数で書き、babelなどを使ってfunctionにトランスパイルすることが多いと思います。