Core JavaScript 3.this

この記事はweb開発に欠かせないJavaScriptのコアな概念をしっかり理解しようということでCore Javascript(韓国語)を要約した内容になります。

3. this

目次

概要

状況によって変わるthis

グローバル領域のthis

グローバル領域でのthisはグローバルオブジェクトを指す。 ブラウザ環境では window、Node.jsではglobalを指すことになる。

グルーバル変数とグローバルオブジェクト

var hello = 'こんにちは';
console.log(hello);         //こんにちは
console.log(window.hello); //こんにちは
console.log(this.hello);   //こんにちは

グローバル変数を宣言すると、JavaScriptエンジンがグローバルオブジェクトのプロパーティとして割り当てる。 実は、すべての変数は特定のオブジェクトのプロパーティとして動作する。 その特定のオブジェクトがLexicalEnvironmentだ。 ある弁数を呼び出すとLexicalEnvironmentの中から検索し、その値を返却する。 なのでグローバルオブジェクトをプローパティとして直接割り当ててもvarで宣言したのと同じ結果になる。

メソッドを呼び出す時そのメソッド内部のthis

関数 vs. メソッド

var func = function (x) {
  console.log(this, x);
};
func(1); // window { ... } 1

var obj = {
  method: func
}
obj.method(2); // { method: f } 2

両方funcを実行しているが、それぞれ出力されるthisの結果が異なってくる。 シンプルに言うと、.を付けて実行すると、メソッドとして関数が呼び出される。

メソッド内部でのthis

thisに呼び出し元の情報が割り当てられる。 .が付いているオブジェクトを指す。

var obj = {
  methodA: function () { console.log(this); },
  inner: {
    methodB: function () { console.log(this); }
  }
};
obj.methodA(); // { methodA: f, inner: {...} } (=== obj)
obj.inner.methodB(); // { methodB: f } (=== obj.inner)

関数を呼び出す時その関数内部のthis

関数内部のthis

関数を呼び出す場合はthisが指定されない。 実行コンテキストを構成する時thisが指定されていない場合thisはグローバルオブジェクトを指す。(Chap2で言及された内容) この振る舞いをJavaSCript設計ミスだと指摘する有識者も居る。

メソッドの内部関数でのthis

var obj1 = {
  outer: function () {
    console.log(this); // (1)
    var innerFunc = function () {
      console.log(this); // (2) (3)
    };
    innerFunc();

    var obj2 = {
      innerMethod: innerFunc
    };
    obj2.innerMethod();
  }
};
obj1.outer();

(1)obj1が、(2)ではwindow、そして(3)ではobj2が出力される。 (1)(3)はそれぞれ呼び出し元のオブジェクトが出力されたが、(2)ではグローバルオブジェクトを出力している。 innerFunc()で関数として呼び出しているので、thisが指定されておらずスコープチェーンの最上位オブジェクトであるwindowがバインドされるからだ。

上位スコープのthisを指定する方法

var obj = {
  outer: function () {
    console.log(this); // (1) { outer: f }
    var innerFunc = function () {
      console.log(this); // (2) window
    };
    innerFunc1();

    var self = this;
    var innerFunc2 = function () {
      console.log(self); // (3) { outer: f }
    }
    innerFunc2();
  }
};
obj.outer();
var obj = {
  outer: function () {
    console.log(this); // (1) { outer: f }
    var innerFunc = () => {
      console.log(this); // (2) { outer: f }
    };
    innerFunc();
  }
};
obj.outer();

ES2015(ES6)で導入されたアロー関数は、実行コンテキスト生成時thisをバインドするステップ自体をスキップするので上位スコープのthisをそのまま流用することができる。