tweeeetyのぶろぐ的めも

アウトプットが少なかったダメな自分をアウトプット<br>\(^o^)/

javascriptのthisについて真面目におさらいするメモ

はじめに

thisについても当たり前のことが多いですが、自分用のためにもおさらいメモです
入れ子関数内のthisとか知らないときは本当に悩むものですよね

ながれ

こんな感じの流れでおさらいをまとめます

1.thisが参照するもの
2.thisの決定
3.入れ子関数内のthis
4.thisの束縛
5.コンストラクタ関数内のthis
6.prototypeメソッド内のthis

1.thisが参照するもの

・ブラウザなどのグローバルスコープではwindowオブジェクト
・関数の中ではその関数が格納されているオブジェクト
 ※new、call、applyを使う場合を除く

コードで確認

※ グローバルスコープの場合
console.log(this); 
// 出力 : window

※ 関数の中の場合
var hoge = {
  say: function(){console.log(this);}
};
hoge.say();
// 出力 : Object {say: function}

2.thisの決定

thisの参照は関数が呼ばれたコンテキストによって変わる

コードで確認

var hoge = 'mone';
var hogeObject  = { hoge: 'hogeObject property'};
var whatIsHoge = function(){
  console.log(this.hoge);
}

/* whatIsHoge関数をhogeObjectのwhatIsHogeメソッドにポイントする */
hogeObject.whatIsHoge = whatIsHoge;
hogeObject.whatIsHoge(); // 出力 : hogeObject property
whatIsHoge(); // 出力 : mone

3.入れ子関数内のthis

入れ子関数内でthisが呼ばれた場合は、thisはグローバルオブジェクト(ブラウザならwindow)を参照
※ ECMAScript5ではこの仕様は改修予定らしい

コードで確認

/* 単純な入れ子の場合 */
var piyo = {
  func1: function(){
    console.log(this);
    // 出力 : Object {func1: function}

    var func2 = function(){ console.log(this); }();
    // 出力 : Window
  }
}
piyo.func1();

/* オブジェクトのメソッドに引数で渡した場合 */
var pico = {
  func1: function(func){
    console.log(this);
    // 出力 : Object {func1: function}

    func();
    // 出力 : Window
  }
}
pico.func1( function(){ console.log(this);} );

4.thisの束縛

入れ子内の関数であっても、
スコープチェーンやcall(またはapply)を使うことでthisのコンテキストをコントロールできる

ということで、「3.入れ子関数内のthis」の例を
・スコープチェーンを使って束縛
・call、applyを使って束縛
という形でthisがpiyoオブジェクトをポイントするように変えてみます

スコープチェーンを使って束縛
var piyo = {
  func1: function(){
   /* func1のスコープにある_thisにthisへの参照を保持させる */
    var _this = this;
    console.log(this);
    // 出力 : Object {func1: function}

    var func2 = function(){
      console.log(this);
      // 出力 : Window

      console.log(_this);
      // 出力 : Object {func1: function}
    }();
  }
}
piyo.func1();
call、applyを使って束縛
var piyo = {
  func1: function(){
    var _this = this;
    console.log(this);
    // 出力 : Object {func1: function}

    var func2 = function(){
      console.log(this);
      // 出力 : Object {func1: function}

      console.log(_this);
      // 出力 : Object {func1: function}
    }.call(this); // applyでもOK
  }
}
piyo.func1();
call、applyの使い方

↑は「3.入れ子関数内のthis」の例を元に無理矢理callを使った感があるので
call、applyのおさらい的な感じで使い方もメモ

var hoge = {};
var testFunc = function(arg1, arg2){
  this.prop1 = arg1;
  this.prop2 = arg2;
  console.log(this);
}
testFunc();
// 出力 : Window

testFunc.call(hoge, 'aaa', 'bbb');
// 出力 : Object {prop1: "aaa", prop2: "bbb"}

testFunc.apply(hoge, ['aaa', 'bbb']);
// 出力 : Object {prop1: "aaa", prop2: "bbb"}

5.コンストラクタ関数内でのthis

new演算子でコンストラクタ呼び出しをする場合はthisはインスタンスを参照する

コードで確認

var Animal = function(kind) {
  console.log(this);
  this.kind = kind;
  /* 入れ子関数っぽいけど...? */
  this.sayKind = function(){console.log(this.kind);}
}

/* newをつけてコンストラクタ呼び出し */
var dog = new Animal('dog'); // 出力 : Animal {}
console.log(dog.kind); // 出力 : dog
dog.sayKind(); // 出力 : dog

/* newをつけずに関数呼び出し */
var dame_dog = Animal(); // 出力 : Window
console.log(dame_dog.kind); // Uncaught TypeError
dame_dog.sayKind(); // Uncaught TypeError

6.prototypeメソッド内のthis

prototypeメソッド内のthisはメソッドが呼ばれたインスタンスを参照する

コードで確認する

var Animal = function(kind){
  this.kind = kind;
};
Animal.prototype.sayKind = function(){console.log(this.kind);}

var dog = new Animal('dog');
var cat = new Animal('cat');

dog.sayKind(); // 出力 : dog
cat.sayKind(); // 出力 : cat

開眼!JavaScriptという書籍を読んでいたらよくまとまっていたのでそれをだいぶ参考にして書いてみました
これでthisについては安心ですね!

この記事で参考にした書籍

開眼!  JavaScript ―言語仕様から学ぶJavaScriptの本質
Cody Lindley
オライリージャパン
売り上げランキング: 11,609