本記事では、ES6で矢印関数を使用してはいけない場合について解説します。
結論から言うとアロー関数は、自分自身のこの値や引数のオブジェクトを持たないため、イベントハンドラ、オブジェクトリテラルのメソッド、プロトタイプメソッド、引数オブジェクトを使用する関数がある場合などには使用しないようにしましょう。
以下で詳しく解説します。
1. イベントハンドラーを使用する場合
例えば、次のような入力テキストフィールドがあるとします。
<input type="text" name="username" id="username" placeholder="Enter a username">
そして、ユーザーがユーザー名を入力したときに、挨拶文を表示させたいとします。
以下は、挨拶メッセージを表示するための<div>要素です。
<div id="greeting"></div>
ユーザーがユーザー名を入力したら、入力の現在値を取得し、<div>要素に更新します。
const greeting = document.querySelector('#greeting');
const username = document.querySelector('#username');
username.addEventListener('keyup', () => {
greeting.textContent = 'こんにちは ' + this.value;
});
しかし、このコードを実行すると、何を入力しても次のようなメッセージが表示されます。
こんにちは undefined
イベントハンドラ内のthis.valueは常にundefinedを返すということです。
先に述べたように、arrow関数は独自のthis値を持ちません。上の例では、arrow関数のthisはグローバルオブジェクトを参照しています。
ウェブブラウザの場合、グローバルオブジェクトはwindowです。windowオブジェクトはvalueプロパティを持っていません。そこで、JavaScriptエンジンは、windowオブジェクトにvalueプロパティを追加し、その値をundefinedに設定します。
この問題を解決するには、アロー関数の代わりに通常の関数を使用する必要があります。この場合の「this」は、イベントのトリガーとなる<input>要素に束縛されます。
username.addEventListener('keyup', function () {
input.textContent = 'Hello ' + this.value;
});
2. オブジェクトメソッドを使用する場合
以下のcounterオブジェクトを参照してください。
const counter = {
count: 0,
next: () => ++this.count,
current: () => this.count
};
counterオブジェクトには、current()とnext()という2つのメソッドがあります。current() メソッドは現在のカウンタ値を返し、next() メソッドは次のカウンタ値を返します。
以下は、次のカウンターの値が1であるべきことを示しています。
console.log(counter.next());
しかし、NaNを返します。
これは、オブジェクトの内部でarrow関数を使用した場合、この値を包含するレキシカルスコープ(この例ではグローバルスコープ)から継承するためです。
next()メソッド内のthis.countは、(Webブラウザの)window.countと等価です。
windowオブジェクトはcountプロパティを持っていないため、window.countはデフォルトでundefinedです。そのため、next()メソッドはundefinedに1を足してNaNにしています。
これを解決するには、以下のようにオブジェクト・リテラルのメソッドとして正規の関数を使用します。
const counter = {
count: 0,
next() {
return ++this.count;
},
current() {
return this.count;
}
};
さて、next()メソッドを呼び出すと、期待通り1が返されます。
console.log(counter.next()); // 1
3. Prototypeメソッドを使用する場合
プロトタイプパターンを使用した以下のcounterオブジェクトをご覧ください。
function Counter() {
this.count = 0;
}
Counter.prototype.next = () => {
return this.count;
};
Counter.prototype.current = () => {
return ++this.next;
}
これらのnext()およびcurrent()メソッド内のthis値は、グローバル・オブジェクトを参照しています。
メソッド内の this 値は、counterオブジェクトを参照するようにしたいので、代わりに通常の関数を使用する必要があります。
function Counter() {
this.count = 0;
}
Counter.prototype.next = function () {
return this.count;
};
Counter.prototype.current = function () {
return ++this.next;
}
4. 引数オブジェクトを使用する場合
アロー関数は、引数オブジェクトを持ちません。したがって、引数オブジェクト(arguments)を使用する関数がある場合、アロー関数を使用することはできません。
例えば、次のようなconcat()関数は動作しません。
const concat = (separator) => {
let args = Array.prototype.slice.call(arguments, 1);
return args.join(separator);
}
その代わり、こんな風に普通の関数を使いましょう。
function concat(separator) {
let args = Array.prototype.slice.call(arguments, 1);
return args.join(separator);
}