#this?
this って分かりにくいですよね。
でも、結構単純なんです。
##JavaScriptでのグローバル変数の扱い
this の前にまず先に知っておかないといけない事。
JavaScriptでは、グローバル変数は必ず 何かしら の所属にあります。
[何かしら].[変数名]=''; ということです。
var aaa = 'A';
console.log( aaa ); // 'A'
console.log( window.aaa ); // 'A'
console.log( aaa === window.aaa ) // true。Node.js の場合は global.aaa
グローバルの var aaa='A';
は window.aaa='A';
と同じで、
前者は window.
が省略されている、ということです。
aaa
から見ると、この [何かしら] が this になるというだけです。この場合は windows が this になります。
※ 関数内とかの var aaa はまたちょっと別。
##functionで確認
aaa を function にして確認します。
var aaa = function(){ alert(this === window); };
aaa(); // true。window.aaa() と同じなので this は window になる
なので、
var o = { x:'XXX', f:function(){ alert(this.x); } };
o.f(); // XXX。this は o になる
##色々な方法で確認
var aaa = function(){ alert(this.x); };
var b = { x:'BBB', f:aaa };
var c = { x:'CCC', f:aaa };
b.f(); // BBB
c.f(); // CCC
以下のような使い方もできます。
var o = new function(){
this.x = 'XXX';
this.f = function(){ alert(this.x); };
};
o.f(); // XXX
var aaa = { x:'XYZ' };
aaa.f = o.f; // ↑ の o.f を代入。
aaa.f(); // XYZ。this が aaa になるので aaa.x が使われる。
o.f を aaa.f から呼ぶと this は o ではなく aaa が使われます。
this は付いてく人によって変わっていくんですね。
o.f() なら o だし、aaa.f() だと aaa です。
ただ、.apply()
や .call()
ってチンピラみたいのや、.bind()
というボスのような強力なのもいますので次で。
ちなみに ↑ のコマンド例は良い方法とは思えないです。挙動の確認用として書いたものです。
「友だちの友だちには手を出すな」って誰かが言ってました。プログラムが大きくなるにつれ痛感していきます。
##this の変更( .apply() / .call() )
.apply()
や .call()
というものが昔からいました。this を変えられます。
第一引数が this として使われ、この2つの違いは関数に値を渡す方法が違います。
var o = { x:'XXX', f:function(a,b,c){ alert([this.x, a, b, c].join('-')); } };
o.f('A1','B1','C1'); // XXX-A1-B1-C1
o.f.apply({x:'YYY'}, ['A2','B2','C2']); // YYY-A2-B2-C2。※this が変わってる
o.f.call({x:'ZZZ'}, 'A3','B3','C3'); // ZZZ-A3-B3-C3。※ 〃
var o = new function(){
this.x = 'XXX';
this.f = function(a,b,c){ alert([this.x, a, b, c].join('-')); };
};
o.f('A1','B1','C1'); // XXX-A1-B1-C1
o.f.apply({x:'YYY'}, ['A2','B2','C2']); // YYY-A2-B2-C2。※this が変わってる
o.f.call({x:'ZZZ'}, 'A3','B3','C3'); // ZZZ-A3-B3-C3。※ 〃
呼ばれ方によって、this(コンテキスト) がころころ変わります。
なので、this を固定するために私は以下のようなことをよくやります。
var o = new function(){
var that = this;
that.x = 'XXX';
that.f = function(a,b,c){ alert([that.x, a, b, c].join('-')); };
};
o.f.apply({x:'YYY'}, ['A2','B2','C2']); // XXX-A2-B2-C2
o.f.call({x:'ZZZ'}, 'A3','B3','C3'); // XXX-A3-B3-C3
var f=o.f; f('A4','B4','C4'); // XXX-A4-B4-C4
this は変わるので、別の変数に退避してどう呼ばれようといいようにしています。
##this の変更( .bind() )
後から .bind()
がやってきました。IE9以降です。
こいつを関数に付けて実行すると、this が固定されたその関数が返ってきます。
かなり強力に this を縛り付けます。もう、こいつに this を奪われると奪還は無理です。だぶん。
var o = { x:'XXX', f:function(){ alert(this.x); } };
o.f(); // XXX
o.f.call({ x:'こら!' }); // こら!
o.f = o.f.bind({ x:'(>_<)' }); // ★ .bind()
o.f(); // (>_<)
o.f.call({ x:'おーい!' }); // (>_<)
o.f.apply({ x:'戻ってこい!' }); // (>_<)
var f=o.f; f(); // (>_<)
window.f=o.f; window.f(); // (>_<)
##this が変わらないアロー関数について
※ ご指摘頂きましたので追記しました。ありがとうございます。
関数定義の方法は function(){} と アロー関数 というやつがあって、これは古いブラウザでは
動かないのですがこの アロー関数 は上記で書いたような this の考慮は全く要らず、その場の this が引き継がれます。
(この挙動は、はたして分かり易いんだか、分かり難いんだか…)
(function(){
f1 = ()=>{ console.log(this.x); }; // これが アロー関数 での関数定義。
f2 = function(){ console.log(this.x); }; // 従来の。
}.call({ x:'XXX'}));
window.x = 'window!';
f1(); // ?
f2(); // ?
window.f1();
どうでしょうか?
アロー関数で f1()
が作成された時点での this は { x:'XXX' } なので f1()
は 'XXX' になります。
f2()
はこの場合、window.f2() として動くので this には window が使われ 'window!' となります。
※ f1 も window.f1() として動くけどアロー演算子なので this が違う。
this で最初に?と思うでおなじみの setTimeout の例で書いてみました。
window.x = 'window!';
(function(){
var that = this;
setTimeout(function(){
console.log('a', this.x); // a,window!。this は window が使われる。
console.log('b', that.x); // b,XXX。that はただの変数なので変化しない。
}, 1000);
setTimeout(()=>{ // アロー関数
console.log('c', this.x); // c,XXX。this は変わらない。
}, 2000);
}.call({ x:'XXX' }));
setTimeout で実行される場合、通常なら this
は window
になるのですが、
アロー演算子の場合は ()=>{} がある場所の this
をそのまま使うので上記のような動きになります。