アロー関数とは?
アロー関数の()=>{}
はfunction (){}.bind(this)
と同じ。ただし、functionのように関数内部にオブジェクトを指すthisを生成しない。
サンプルプログラム
function outer(ff) {
setTimeout(ff, 0);
}
class T {
f() {
var obj = {
ff: () => { console.log(this); }
};
outer(obj.ff);
}
}
var t = new T;
t.f();
obj.ffはアロー関数なので関数内部にオブジェクトobjを指すthisを生成しない。
obj.ffはアロー関数なのでfunction(){}.bind(this)と同じ。
関数obj.ffにbindするthisはオブジェクトTになる。
このプログラムはTを出力する。
obj.ffを外側の関数outerに移動させてもTを出力するのはアロー関数がthisをbindしているからと考えられる。1
functionが関数内部にオブジェクトを指すthisを生成するとは?
例1
var obj = {
f: function () { console.log(this); },
g: () => { console.log(this); }
}
obj.f();
obj.g();
例2
function Obj() { }
Obj.prototype.f = function () { console.log(this); }
Obj.prototype.g = () => { console.log(this); }
obj = new Obj;
obj.f();
obj.g();
例1も例2も通常関数fは関数内部にオブジェクトobjを指すthisを生成し、thisがobjを指す。
アロー関数gは関数内部にオブジェクトobjを指すthisを生成しないので、thisがWindowを指す。
obj を指す。
アロー関数 g は関数内部にオブジェクト obj を指す this を生成しないので、this が window を指す。
function が関数内部にwindowオブジェクトを指す this を生成しない特殊なケース
class P {
a() {
return function () {
console.log(this); // undefined
};
}
}
new P().a()();
クラス内でfunctionをreturnしたときだけなぜかthisがundefinedになる。
function Q() { }
Q.prototype.a = function () {
return function () {
console.log(this); // window
};
};
new Q().a()();
prototypeでオブジェクトを作成したときはいつも通りthisがwindowになる。
function関数とthis
function関数の内部にオブジェクトを指すthisを生成する関数の呼び出し形式は、次の4通りが判明している。
-
obj.method()
の形式でfunction関数を呼んだ場合のmethod内 -
obj["method"]()
の形式でfunction関数を呼んだ場合のmethod内 -
new Class
の形式で呼んだの場合のconstructor内 -
new function
の形式で呼んだ場合のfunction内
これら以外の形式で読んだ場合、function関数の内部にあるthisはオブジェクトではなくWindowを指してしまう。
たとえば
var f2 = obj.f;
f2()
としたら、f2()
はobj.f()
の形式で関数を呼んでいないので、例1も例2も関数内部のthisがオブジェクトobjではなくWindowになってしまう。
bindとは?
var f = function () {
console.log(this);
}.bind({ a: 1 })
setTimeout(f, 0);
bindは関数内部のthisをオブジェクト{a:1}で束縛し固定する。
このプログラムはthisとしてオブジェクト{a:1}を出力する。
次のようにすれば bind を自作できる2。
Function.prototype.mybind = function(obj) {
var _this = this;
return function(...args) {
return _this.apply(obj, args);
};
};
bind が束縛するとは、クロージャーを作って function 関数内の this がオブジェクト obj を指すようにすることだとわかる。
addEventListenerとsetTimeoutが上書きするthisは何か
event_target.addEventListener('click', function (e) {
console.log(this === e.currentTarget); // true
console.log(this === event_target); // true
});
event_target.addEventListener(type, listener)
があったとき、addEventListener関数はlistener.apply(event_target)
をして、listener関数内のthisをevent_targetに上書きする。
thisとevent_targetとe.currentTargetは同じ値になる。
class T {
fun() {
console.log(this); // Tを返す
setTimeout(function () {
console.log(this); // Windowを返す
}, 0);
setTimeout(()=> {
console.log(this); // Tを返す。setTimeoutがapplyするthisではなく、アロー関数がbindするthisが有効になる。
}, 0);
}
}
new T().fun();
setTimeout(timeout_fun, delay)
があったとき、setTimeout関数はtimeout_fun.apply(window)
をして、timeout_fun関数内のthisをwindowに上書きする。
-
setTimeout関数やaddEventlister関数はthisを書き変えることで有名。 ↩
-
MDN Web Docsにある bind のポリフィルは new や引数にも対応している。 ↩