LoginSignup
22
26

More than 3 years have passed since last update.

JavaScriptのthis(コンテキスト)とは?

Last updated at Posted at 2018-11-06

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 で実行される場合、通常なら thiswindow になるのですが、
アロー演算子の場合は ()=>{} がある場所の this をそのまま使うので上記のような動きになります。

22
26
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
22
26