TL;DR

JavaScript の this は、関数がどのように呼ばれたかで決まる値である。トップレベルではグローバルオブジェクト、メソッドではレシーバのオブジェクトを指す。関数の中では strict モードかどうかで変わる。

仕組み

ES5 からは、bind メソッドで、関数の呼ばれ方に関係なく this の値を固定した新しい関数を作れるようになった。ES2015 からは、自身では this を結び付けないアロー関数が導入された。アロー関数は、それを囲む構文上のコンテキストの this を保持する。

トップレベル

ファイル直下(クラスや関数の外側)では、strict モードかどうかに関わらず、this はグローバルオブジェクトを指す。ブラウザでは window、Node.js では global である。

// ウェブブラウザーでは window オブジェクトもグローバルオブジェクト
console.log(this === window) // true
 
a = 37
console.log(window.a) // 37
 
this.b = "MDN"
console.log(window.b) // "MDN"
console.log(b) // "MDN"

関数

strict モードでなければ、this はグローバルオブジェクトを指す。strict モードでは undefined になる。

function f1() {
  return this
}
 
// ブラウザー上
f1() === window // true
 
// Node 上
f1() === global // true
 
function f2() {
  "use strict" // strict モードにする
  return this
}
 
f2() === undefined // true

call や apply を使うと、this として使うオブジェクトを指定できる。bind は新しい関数を返すが、call と apply はその場で関数を呼び出す。

function add(c, d) {
  return this.a + this.b + c + d
}
 
var o = { a: 1, b: 3 }
 
add.call(o, 5, 7) // 16
add.apply(o, [10, 20]) // 34

メソッド

関数がオブジェクトのメソッドとして呼ばれたとき、this にはメソッドを呼び出したオブジェクト(レシーバ)が入る。

var o = { prop: 37 }
 
function independent() {
  return this.prop
}
 
o.f = independent
 
console.log(o.f()) // 37

クラス

クラスは関数の機能なので、this の動きはほぼ関数と同じである。ただし、コンストラクタの中では this は生成されるオブジェクト自身を指す。インスタンスメソッドは this のプロトタイプに追加され、静的メソッドはクラス自身のプロパティになる。

class Example {
  constructor() {
    const proto = Object.getPrototypeOf(this)
    console.log(Object.getOwnPropertyNames(proto))
  }
  first() {}
  second() {}
  static third() {}
}
 
new Example() // ['constructor', 'first', 'second']

注意点

関数の呼ばれ方によって this が変わるので、予期しない挙動になりやすい。アロー関数を使うか、bind で this を固定すると防げる。

関連

出典