水無瀬のプログラミング日記

プログラムの勉強した時のメモ

Javascriptのthisまとめ

はじめに

JavaScriptを触ってるけど、thisの使い方は特に意識しないでやってきた。
thisの挙動が複雑だとか4種類あるとかは聞いたことあったけど、
その問題に直面することがなかったから無視してたというのが本音。
天からthisから逃げるなと言われてる気がしたので、いい加減に調べてまとめることにする。

this4種

JavaScriptのthisは以下の4つ。
それぞれについて簡単な例とともにまとめる。

  • 関数(function)呼び出しパターン
  • メソッド呼び出しパターン
  • コンストラクタパターン
  • apply,bind,callパターン

関数(function)呼び出しパターン

関数を呼ぶ際のthisは基本的にはglobalオブジェクトを指す。
ただし、use strictを指定しているとundefinedになる。

// 'use strict'なしパターン 
function test() { 
    console.log(this); 
} 
test(); // Window{...} 
// 'use strict'ありパターン 
'use strict' 
  
function test() { 
    console.log(this); 
} 
test(); // undefined 

メソッド呼び出しパターン

関数もメソッドも同じだろって感じだけど実は違う。 下記のようなパターンのこと。

const obj = { 
    test: function() { 
        console.log(this); 
    } 
} 
obj.test(); // { test: [Function: test] } 

ちなみにこのthisをどうしてもglobalとして使いたいなら下記のようにする。

const obj = { 
    test: function() { 
        console.log(this); 
    } 
} 
// 下記パターンどちらでもglobalになる 
// パターン1 
const globalThis = obj.test; 
globalThis(); // Window{...} 
// パターン2 
(0, obj.test)(); // Window{...} 

コンストラクタパターン

コンストラクタのthisは生成するobject(自分自身)を指すようになる。
何言ってるかわからなくても、見ればなんとなくわかるはず。

class Test { 
    constructor(value){ 
        this.value = value; // 自身(Test)のvalueに引数のvalueを入れてる 
    } 
} 
const newClass = new Test(10); 
console.log(newClass.value); // 自身のvalueにちゃんと入っている 

ちょっと古いクラスの書き方するなら下のような感じ。
もちろん結果は同じ。 ただしこの書き方だと、あくまで関数なのでnewを忘れるとthisはglobalを指すようになってしまう。
おとなしくclassと付けるのがいいと思う。

function Test(value) { 
    this.value = value; 
} 
const newClass = new Test(10); 
console.log(newClass.value); 

apply,bind,callパターン

apply,call

applyとcallは共にthisの内容を設定できるようにするもの。
下記のようなパターンのこと。

function test() { 
    console.log(this); 
} 
const obj = { name: 'test' }; 
test(); 
// testのthisをobjに設定 
test.call(obj); // obj{...} 
test.apply(obj); // obj{...} 

applyとcallの違いは引数のとり方が違う。
applyは配列、callはそのままといった具合。

const testObj = { 
    test: function(value1, value2) { 
        console.log('this:', this); 
        console.log('value1:', value1); 
        console.log('value2:', value2); 
    } 
} 
const obj = { name: 'test' }; 
testObj.test.apply(obj, [1, 3]); 
testObj.test.call(obj, 1, 3); 
# 実行結果 
this: { name: 'test' } 
value1: 1 
value2: 3 
this: { name: 'test' } 
value1: 1 
value2: 3 

bind

ここで参照するthisはこれじゃなきゃ嫌なんだ!という時に使う。
要は強制的にthisの内容を紐付けてやること。

function test() { 
    console.log(this); 
} 
const obj = { name: 'test' }; 
// testのthisをobjにする 
const bid = test.bind(obj); 
test(); // こっちはglobal参照 
bind(); // こっちはobj参照 

おまけ(アロー関数)

アロー関数はthisを変えたりしない。
変に値が変わらない分わかりやすいかも知れない。

const obj = { 
    func: function(){ 
        console.log(this); 
    }, 
    arrow: () => { 
        console.log(this); 
    } 
} 
obj.func(); // { func: [Function: func], arrow: [Function: arrow] } 
obj.arrow(); // Windw{...} 

まとめ

ようやく向き合ったthisは思ってたよりも理解不能じゃなかった。
理解不能ではなかったけど、わかりやすく説明するのは無理だなって思った。
今度はprototype辺りとも向き合いたい。