Webエンジニア hakshuの部屋

のらりくらりと綴る

JavaScriptのthisとは

ふと

JavaScript使ってきてるわりにthisのコンテキストが変わることについて、あんまり理解できてないなーと思ったのでMDN読みながらまとめる

developer.mozilla.org

thisとは?

JavaScriptでは式の1つで、関数の実行コンテキストを参照するものである
だが、呼び出し方やアロー関数などでもコンテキストが変わるのでややこしくなっている

use strictとそれ以外でまず異なる

  • use strict (厳格)モード
    • どのような値でも取り得る
  • 非厳格モード
    • 実行コンテキスト(グローバル、関数、eval)のプロパティで常にオブジェクトへの参照である

グローバルコンテキストの場合

グローバルコンテキスト(トップレベル)ではthisはグローバルオブジェクトを参照する

console.log(this === window); // true

関数コンテキスト

関数内のthisは関数の呼び出され方と厳格モードの有無によって異なる

非厳格モード

呼び出し時にthisの値が設定されない場合規定のグローバルオブジェクトとなる

function func() {
  return this;
}

func() === window; // true

厳格モード

呼び出し時にthisの値が設定されていない、つまり関数がオブジェクトに属していない形で呼び出されるとundefinedのままになる

function func2() {
  'use strict';
  return this;
}

console.log(func2() === window); // false
console.log(func2() === undefined); // true

関数の呼び出し時にthisの値を特定の値に設定したい場合にはcallやapplyを使用して呼び出す

function func3() {
  'use strict';
  return this;
}

console.log(func3.call(window) === window); // true
console.log(func3.apply(window) === window); // true

また、bindで永続的に設定した関数を生成できる

const newFunc = func3.bind(window);
console.log(newFunc() === window); // true

Arrow Functionの場合

Arrow Functionの場合のthisは実行時ではなく関数の定義時に決まる
そのthisは自身の外側のスコープに定義されたもっとも近い関数のthisの値を指す
注意点としてArrow Functionはcall, apply, bindでthisを設定することはできない

const arrowFn = () => {
  // この時外側には関数は存在しないためトップレベルのthis、つまりwindowを指す
  return this;
};

console.log(arrowFn() === window); // true
console.log(arrowFn.call({ a: 1 })); // Window {...}

クラスコンテキスト

基本クラスの場合thisはそのクラスを指す また、クラス内の非staticなメソッドはthisのプロトタイプに追加される

class Foo {
  constructor() {
    console.log(this); // Foo {}
    const proto = Object.getPrototypeOf(this);
    console.log(Object.getOwnPropertyNames(proto)); // ['constructor', 'bar']
  }
  bar() {}
  static hoge() {}
}

new Foo();

子孫クラス

継承先の子孫クラスではthisバインディングはなく、super()を呼び出すとコンストラクタ内にthisバインディングが作成される
このthisはsuperを呼び出したクラス自身を指す

class Base {
  base() {}
}
class Child extends Base {
  constructor() {
    super();
    console.log(this); // Child
  }
}
new Child();

オブジェクトのメソッドの場合

オブジェクトのメソッドとして呼び出されるときはthisはメソッドの所属オブジェクトになる

const obj = {
  count: 10,
  currentCount: function() {
    return this.count;
  }
};

console.log(obj.currentCount()); // 10

まとめ

  • 基本的にはthisは呼び出し時の所属オブジェクトを指す
  • Arrow Functionのthisは呼び出し時ではなく定義時の外側のスコープに定義されたもっとも近い関数のthisを指す
  • 関数コンテキストでは厳格モードと非厳格でthisが異なる

JavaScript Primerにもコールバックなどさらに詳しい情報が載っているのでこちらも読んでみようと思います jsprimer.net