最近Chrome拡張機能を作り始めて、Javascriptに触れる機会が増えた。
QiitaやらMozillaのサイトを見てると、Javascriptはモダンな"書き方"というのがどんどん変わってきているらしい。
そのうちの一つがアロー関数という関数の書き方
1
2
3
4
5
|
fucntion test() {
console.log("hoge");
}
test();
|
1
2
3
|
const test = _ => {
console.log("hoge");
};
|
上の2つのコードはほぼ同じ意味で、下がアロー関数
アロー関数とfunctionの違い??
本題
この2つに違いはあるか?
ある。
thisのスコープが異なる
アロー関数はthisがレキシカルスコープ
つまり、宣言時のスコープに固定される
functionは、thisが実行時のスコープになる
どういうことか、試してみるとわかりやすい
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
var name = 'Adam'
function Person(name) {
this.name = name
}
Person.prototype.sayNameArrow = _ => {
console.log(`I'm ${this.name}`)
}
Person.prototype.sayNameNotArrow = function() {
console.log(`I'm ${this.name}`)
}
const bob = new Person('Bob')
bob.sayNameArrow() // I'm Adam.
bob.sayNameNotArrow() // I'm Bob.
|
sayNameArrow()
はアロー関数なので宣言時のthisが保存される。
なので、Adamがthis.nameになる。
一方、sayNameNotArrow()
はfunctionなのでthisは呼び出しもとのスコープになる。
つまりグローバルスコープだからthis.nameはBobになる
クラスメソッドをイベントハンドラーとして使いたい
自分の場合は、クラスのメソッドをDOMのイベントハンドラーに設定しようとしたときこれに気づいた。
例えばページ上でなんかボタンが押されたら
あるオブジェクトのメソッドが呼ばれるみたいなことをしたかったのだが、this.〜でオブジェクトのプロパティが読めずうまく行かなかった。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
class Test {
constructor(obj) {
this.name = "TEST";
this.testArrow = _ => {
console.log(this.name)
}
this.setEventHandler(obj);
}
testMethod() {
console.log(this.name);
}
setEventHandler(obj) {
obj.addEventListener('click', this.testMethod);
obj.addEventListener('click', this.testArrow);
}
}
const button = document.getElementById('some-button');
const t = new Test(button);
button.click();
// Output:
// undefined (<- testMethod)
// TEST (<- testArrow)
|
上記の例で言うと、ボタンが押されるとtestMethod
とtestArrow
がそれぞれ呼ばれるが、testMethodの中で使われるthis.nameは呼び出された場所によって変わってしまう。
testArrowだと宣言時のthisに固定される。
つまり
クラスメソッドをイベントハンドラーとして使いたい場合は、メソッドではなくアロー関数をつかうといい
あとで知ったことだが、functionのスコープを固定したければbind
を使うのも有効らしい。
1
2
3
|
const unbound = test.testMethod;
unbound.bind(test);
unbound(); // "TEST"
|
bind()にオブジェクトを渡すと、それをthisとして扱ってくれる。