【javascript】thisを指しているものがわからなくなる時の対処法

フォームをバリデーションするのにオススメのjQueryプラグイン 9選

javascriptのスキルを1段階上になりたいなと思っていて、本を読んだり、コードを書いたりしているんですが、「this」を使っていて思ったものを指していないときがあるんですよね。これは「this」を理解できていないんだなってことで今回はjavascriptの「this」にフォーカスを当てて勉強してみようと思います。

Ads

thisを指しているものがわからなくなる時がある

javascriptを書いていると「this」が何を指すかわからなくなる時があるんですよね。

例えばこんなの

var Winner = function(team){
	this.team = team;
};

Winner.prototype.sayTeam = function(){
	console.log(this);
	var teamName = function(){
		console.log(this);
	}();
};

var juve = new Winner('ユベントス');
var fcb = new Winner('バルセロナ');

juve.sayTeam();
fcb.sayTeam();

sayTeam内のthisはWinnerを指していますね。だけど、teamName関数のthisはwindowを指しています。
自分の感覚的には、teamName関数のthisも「Winner」を指していると思っていました。これが、混乱の元なんですよね。感覚的にthisが指すものはこれかなって思っていたのに挙動が違う。thisが指しているものが思っていたものと違う。

「this」について全然理解できていない初級者なので、「this」にスポットを当ててお勉強しました。
勉強するにあたり、いろんな書籍のthisの説明を読みました。その中で開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質が一番わかりやすかったです。本当にこれおすすめ。めちゃくちゃわかりやすいです。書籍については後ほどご紹介しますね。

そもそもthisって何なのさ

関数が実行されるときに、thisの値が設定されます。このときに設定される値が、関数を呼び出すオブジェクトへのリンクです。さらにいうとthisに何が入るかは関数を実行するまで決まっていなくて、関数が実行時に呼び出される状況によります。

「this」が参照するもの

・ブラウザなどのグローバルスコープではwindowオブジェクト
・関数の中ではその関数が格納されているオブジェクト
※関数がメソッドではなく関数として呼び出された場合、その中のthisはグローバルオブジェクトを指す
※new、call、applyを使う場合を除く

グローバルスコープの場合

console.log(this);
// window

うん、これはwindowオブジェクト指すよな。これは混乱しない。

関数の中の場合1

var obj = {
	func1: function(){
		console.log(this);
	}
};
obj.func1();

関数の中ではその関数が格納されているオブジェクトってことだから「this」はオブジェクトobjを指してるってことですね。

関数の中の場合2

var func2 = function(){
	console.log(this);
};

func2();

これは関数がメソッドではなく関数として呼び出されているからこの場合の「this」はグローバルオブジェクトwindowですね。なんかだいぶわかってきた気がします。

入れ子関数内では、thisはグローバルオブジェクトを参照する

これはさっき例に出させてもらった僕が混乱したパターンのやつです。

Winner.prototype.sayTeam = function(){
	console.log(this);// Winnerオブジェクト
	var teamName = function(){
		console.log(this);// windowオブジェクト
	}();
};

これに関してはそうゆう仕様なんだと覚えておくのが一番かもしれません。ECMAScript5ではこの仕様が修正されるみたいですね。thisが呼ばれる関数が他の関数に内包されている場合や、他の関数コンテクストからthisが呼ばれた場合にはそのthisはグローバルオブジェクトを参照します。

これって要するにthisを見失ってしまっている状態なんですよね。ってことで次で見失っている場合の対処方法を紹介します。

thisを見失う問題の回避方法

これは結構簡単な方法でできます。要は、thisを見失わないように、親の関数にthisへの参照を保存しておけばいいんです。

var obj = {
	property: 'プロパティ',
	myMethod: function(){
		var that = this;
		var func1 = function(){
			console.log(that.property);
			console.log(this);
		}();
	}
};
obj.myMethod();

変数thatにthisの値が保存されているのでfunc1でobj.myMethodにアクセスできます。これでthisを見失わずに済みますね。

call()やapply()でthisの値をコントロールできる

thisの値は関数が呼ばれた状況によって決定されると書きました。ただ、call()やapply()をつかうことでthisの値をコントロールできます。

var obj = {};

var func = function(param1, param2){
	this.foo = param1;
	this.bar = param2;
	console.log(this);
};
func.call(obj, 'foo', 'bar');// callの場合
// func.apply(obj, ['foo', 'bar']);// applyの場合
console.log(obj);

上記のようにcallもしくはapplyを使うことでthisの値を自分で設定できます。上記の場合だとfuncはwindowオブジェクトを指すはずですが、callを使うことでobjを参照できるようになっています。

thisをコンストラクタ関数で使う

newを使ってコンストラクタ関数を呼び出す場合は、thisの値はコンストラクタ関数内で新しく造られるインスタンスを参照します。これは今までとちょっと違う感じですね。

var obj = function(name){
	this.name = name;
};
var hoge = new obj('hogehoge');
console.log(hoge.name);

objのインスタンスを作るときに、コンストラクタに渡された引数(nameの値)をthis.nameに設定することで、新しいインスタンスのnameプロパティに格納しています。newを使った場合のthisはこれから生成されるオブジェクトになるっていうのを覚えておくようにしましょう。

プロトタイプメソッド内のthisは生成されるインスタンスを参照する

var Person = function(x){
	if(x){
		this.fullName = x;
	}
};

Person.prototype.whatIsMyFullName = function(){
	return this.fullName;
};

var cody = new Person('cody lindley');
var lisa = new Person('lisa lindley');

console.log(cody.whatIsMyFullName(), lisa.whatIsMyFullName());

cody.whatIsMyFullName()もlisa.whatIsMyFullName()もどちらもオブジェクトのPersonのfullNameを参照しているのがわかりますね。whatIsMyFullNameメソッドを呼び出したインスタンスを参照しているんですね。

thisがわかった気がする

いろいろ検証したり、調べたり、本を読んだりしましたが、関数の中ではその関数が格納されているオブジェクトっていうので何となく腑に落ちた感がありました。関数が呼び出されたときにthisが渡されるわけだけども、関数として呼び出された場合はwindowオブジェクトが格納されているオブジェクトってことなんですね。多分。

この辺がわかっていればとりあえず今後は悩まなくて済みそうな気がします。対処法もわかったし。

こうゆう感じのjavascriptのコードでもthisが理解できそうです。

function AppView (el){

    // 初期化
    this.initialize(el);

    // イベント
    this.handleEvents();
}

AppView.prototype.initialize = function(el){
    this.$el = $(el);
};

AppView.prototype.handleEvents = function(){
    var self = this;
    this.$el.on('keyup', function(e){
        self.onKeyup(e);
    });
};

AppView.prototype.onKeyup = function(e){
    var $target = $(e.currentTarget);
    this.model.set($target.val());
};

thisがわかるようになってきました。何を指しているのか理解するだけで見通しがかなりよくなった気がします。
よーし、この調子でjavascriptの何かにフォーカスを当てて勉強しよう。

次は、スコープとクロージャあたりかな〜

参考にした書籍


開眼! JavaScript ―言語仕様から学ぶJavaScriptの本質

javascriptの書籍を何冊か持っているんですが、一番わかりやすくて「なるほど〜」って思える本でした。ただ、ある程度はjavascriptの基本的な部分を理解してさらにステップアップするための最初の一冊という感じです。中級者を目指す初級者におすすめの本ですね。僕ももっと読み込んでjavascriptの理解を深めたいと思います。

いいなと思ったらシェアお願いします

同じタグで検索

Ads
ページの先頭へ