JavaScriptのthisの覚え方


JavaScriptの`this`は同じソースコードでも呼び出し元次第で意味が違ったりして複雑だと思われがちだけど、一回覚えてしまえば簡単だ。

**JavaScriptにはthisが4種類ある**

これだけをしっかり覚えておけば、後は必要な時に *4種類って何があるんだっけ？* と考えれば容易に思い出せる。

ちなみに、下記のコードはブラウザ上で実行することを想定している。(なので`window`を使う)

# トップレベルのthis

グローバルオブジェクトを指す。

```js
var hoge = "fuga";
window.foo = "bar";

// fuga+bar と表示される
console.log(this.hoge + "+" + this.foo);

(function(){
	// 同じくfuga+bar と表示される
	console.log(this.hoge + "+" + this.foo);
})();
```

# コンストラクタ内のthis

作られるインスタンス自身を指す。

```js
var Hoge = function Hoge(msg) {
	this.hoge = msg;
	this.method = function () {
		console.log("method+" + this.hoge);
	}
}

var obj1 = new Hoge("Hello");
var obj2 = new Hoge("World");

// Hello と表示
console.log(obj1.hoge);
// method+Hello と表示
obj1.method();

// World と表示
console.log(obj2.hoge);
// method+World と表示
obj2.method();

// しまったnewを付け忘れたのでHogeの中のthisはグローバルオブジェクトだ！
Hoge("newじゃねーよ!");
// ぎゃーっ！ true！
console.log(window.hoge == "newじゃねーよ!");
// ぎゃーっ！ method+newじゃねーよ!
window.method();
```

# 何かに所属している時のthis

所属しているオブジェクトを指す。

```js
var hoge = {foo:"bar"};
hoge.print1 = function() {
	console.log(this.foo);
};

// bar を表示
hoge.print1();

var func = function() {
	console.log(this.foo);
};
// window.foo が参照されるのでundefined
func();

hoge.print2 = func;
// thisがhogeに変わったので bar と表示される
hoge.print2();

// 一応prototypeも試してみましょうね。
var Hoge = function Hoge(msg) {
	this.message = msg;
};
Hoge.prototype.print = function() {
	console.log("prototype+" + this.message);
}
var obj = new Hoge("Hello");
// prototype+Hello と表示
obj.print();
```

# function#apply とか function#call とかで無理矢理変えられた時のthis

thisを外部からの干渉で無理矢理書き換えられた場合。jQuery使っててclickイベントとか取るとthisが書き換えられている。そういうAPIなの。JavaScriptはNTR文化なの。

あと、`document.getElementsByTagName("div")` とかやると NodeList が取れる。そこで `Array.prototype.slice.call(document.getElementsByTagName("div"))` とかすると Array に変換できるハックがある。ってりんごが言ってた。

```js
var hoge = {
	foo: "bar",
	print:function(){
		console.log(this.foo);
	}
};
// 何かに所属している時のthisなので bar と表示
hoge.print();

var paramour = {foo:"NTR"};
// thisをすり替えられたので NTR と表示
hoge.print.call(paramour);

var Hoge = function Hoge(msg) {
	this.message = msg;
}
Hoge.prototype.print = function() {
	console.log("Love " + this.message);
}

var obj1 = new Hoge("sarari-man");
// Love sarari-man と表示
obj1.print();
// アイエエエエ！？ニンジャ！？ニンジャナンデ！？
obj1.print.call({message:"ninja"});

// thisを保護する方法もあるよ
var ProtectedHoge = function ProtectedHoge(msg) {
	this.message = msg;
	// this を保存しておく
	var _this = this;
	this.print = (function(fn) {
		// fn に元々のthis.printが入ってる。
		return function() {
			// 元々のthis.printを_thisに保存しておいた元々のthisを指定して実行しなおす。
			fn.apply(_this, arguments);
		};
	})(this.print);
}
ProtectedHoge.prototype.print = function() {
	console.log("Love " + this.message);
}

var obj2 = new ProtectedHoge("NinjaSlayer");
// ドーモ、ニンジャスレイヤーです。ニンジャ殺すべし。
obj2.print.call({message:"ninja"});
```

ちなみにCoffeeScriptだとthisの保護も超カンタン `->` じゃなくて `=>` 使うだけ。[コンパイル後のコード](http://coffeescript.org/#try:class%20ProtectedHoge%0A%20%20constructor%3A%28%40message%29%20-%3E%0A%20%20print%3A%28%29%20%3D%3E%0A%20%20%20%20console.log%28%40message%29%0A%0Anew%20ProtectedHoge%28%22Fujikido%22%29.print%28%29%0A)

```coffeescript
class ProtectedHoge
  constructor:(@message) ->
  print:() =>
    console.log(@message)

new ProtectedHoge("Fujikido").print()
```

# ねっ？簡単でしょう？

呼び出し元からthisを変えられる仕様にしたヤツを腹を切れ（#^ω^）ﾋﾞｷﾋﾞｷ