【JavaScript】これ(this)はなに?
JavaScriptでプログラムを組んだり人の作ったコードを読んでいると、よく出てくる“this”という表現。
これ、例えば同一の関数内のはずなのに、そのときどきで指しているものが異なったりして結構わかりにくい、と感じる方も多いのではないでしょうか。
■同一関数内で'this'が指すものが異なる例
これ、例えば同一の関数内のはずなのに、そのときどきで指しているものが異なったりして結構わかりにくい、と感じる方も多いのではないでしょうか。
■同一関数内で'this'が指すものが異なる例
function whatisthis(obj) {
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
}
var obj={
obj_whatisthis : whatisthis
};
var ref_whatisthis = obj.obj_whatisthis;
var obj2={
ref2_whatisthis : obj.obj_whatisthis
};
//(1)関数として呼び出し
/* 1-1 */ whatisthis(obj); // [A]true [B]false
/* 1-2 */ obj.obj_whatisthis(obj); // [A]false [B]true
/* 1-3 */ ref_whatisthis(obj); // [A]true [B]false
/* 1-4 */ obj2.ref2_whatisthis(obj); // [A]false [B]false
//(2)コンストラクタ(constructor)として呼び出し
/* 2-1 */ new whatisthis(obj); // [A]false [B]false
/* 2-2 */ new obj.obj_whatisthis(obj); // [A]false [B]false
/* 2-3 */ new ref_whatisthis(obj); // [A]false [B]false
/* 2-4 */ new obj2.ref2_whatisthis(obj); // [A]false [B]false
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
}
var obj={
obj_whatisthis : whatisthis
};
var ref_whatisthis = obj.obj_whatisthis;
var obj2={
ref2_whatisthis : obj.obj_whatisthis
};
//(1)関数として呼び出し
/* 1-1 */ whatisthis(obj); // [A]true [B]false
/* 1-2 */ obj.obj_whatisthis(obj); // [A]false [B]true
/* 1-3 */ ref_whatisthis(obj); // [A]true [B]false
/* 1-4 */ obj2.ref2_whatisthis(obj); // [A]false [B]false
//(2)コンストラクタ(constructor)として呼び出し
/* 2-1 */ new whatisthis(obj); // [A]false [B]false
/* 2-2 */ new obj.obj_whatisthis(obj); // [A]false [B]false
/* 2-3 */ new ref_whatisthis(obj); // [A]false [B]false
/* 2-4 */ new obj2.ref2_whatisthis(obj); // [A]false [B]false
私的には、結局のところ(関数オブジェクトの実体がどこにあるかにはよらず)実際に呼び出されるときに、
また、補足事項としては、
というルールに集約されるのかな、と理解しています。
- あるオブジェクトのプロパティとして関数が呼び出される場合、thisは当該オブジェクトを指す。
- 上記以外の場合には、thisはGlobalオブジェクト(ブラウザの実装では通常"window")を指す。
また、補足事項としては、
のようになるのかな、と。
- 関数がコンストラクタ(constructor)として呼ばれた場合(var obj=new <関数名>()のようにnewを付けて呼びだした場合)、その関数は新規に作成されたオブジェクトのプロパティとして呼び出される。
従って、ルール1から関数内のthisは新規に作成されたオブジェクトを指す。- 関数がcallもしくはapplyで呼びだされた場合(<関数名>.call(<オブジェクト>,<引数1>[,<引数2>...])、もしくは、<関数名>.apply(<オブジェクト>,<引数の配列>))、その関数はcallもしくはapplyの第1引数(<オブジェクト>)に指定されたオブジェクトのプロパティとして呼び出される。
従って、ルール1から関数内のthisは第1引数で指定されたオブジェクトとなる(言い換えると、関数内で"this"として扱いたいオブジェクトをcallもしくはapplyの第1引数で明示出来る)。- Global領域で'function <関数名>(){...}'もしくは'var <関数名>=function(){...}'で定義された関数は、Globalオブジェクト("window")のプロパティとなる。
従って(この関数が<関数名>を直接指定して呼び出される場合)ルール1から関数内のthisはGlobalオブジェクトとなる。- 関数内で'function <関数名>(){...}'もしくは'var <関数名>=function(){...}'で定義された関数は、オブジェクトのプロパティではない。
従って(この関数が<関数名>を直接指定して呼び出される場合)ルール2から関数内のthisはGlobalオブジェクトとなる。
例えば、上の方で挙げた例(関数whatisthis()はGlobal領域で定義された関数)では、
- 1-1
→"whatisthis"がGlobalオブジェクトのプロパティであるため、"this"はGlobalオブジェクト("window")。 - 1-2
→"obj_whatisthis"が"obj"オブジェクトのプロパティであるため、"this"は"obj"オブジェクト。 - 1-3
→"ref_whatisthis"がGlobalオブジェクトのプロパティであるため、"this"はGlobalオブジェクト("window")。 - 1-4
→"ref2_whatisthis"が"obj2"オブジェクトのプロパティであるため、"this"は"obj2"オブジェクト。 - 2-x
→関数がコンストラクタとして呼び出される場合、新規に作成されたオブジェクトのプロパティとしての呼び出しとなる。従って、"this"は新規に作成されたオブジェクトを指す。
もう少し複雑な例をあげると、
まず、「(1)関数として呼び出し」た場合には、"test_whatisthis"内でのthisはGlobalオブジェクト("window")になります。
この前提で、
この前提で、
function test_whatisthis() {
function whatisthis(obj) {
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
}
this.whatisthis=whatisthis;
whatisthis(this); // #1
this.whatisthis(this); // #2
whatisthis.call(this,this); // #3
var obj={whatisthis:whatisthis};
whatisthis(obj); // #4
obj.whatisthis(obj); // #5
whatisthis.call(obj,obj); // #6
}
//(1)関数として呼び出し
test_whatisthis();
// #1:[A]true [B]true
// #2:[A]true [B]true ※IE6では [A]false [B]false ??
// #3:[A]true [B]true
// #4:[A]true [B]false
// #5:[A]false [B]true
// #6:[A]false [B]true
//(2)コンストラクタ(constructor)として呼び出し
var temp=new test_whatisthis();
// #1:[A]true [B]false
// #2:[A]false [B]true
// #3:[A]false [B]true
// #4:[A]true [B]false
// #5:[A]false [B]true
// #6:[A]false [B]true
関数test_whatisthis()はGlobal領域で定義されたものです。function whatisthis(obj) {
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
}
this.whatisthis=whatisthis;
whatisthis(this); // #1
this.whatisthis(this); // #2
whatisthis.call(this,this); // #3
var obj={whatisthis:whatisthis};
whatisthis(obj); // #4
obj.whatisthis(obj); // #5
whatisthis.call(obj,obj); // #6
}
//(1)関数として呼び出し
test_whatisthis();
// #1:[A]true [B]true
// #2:[A]true [B]true ※IE6では [A]false [B]false ??
// #3:[A]true [B]true
// #4:[A]true [B]false
// #5:[A]false [B]true
// #6:[A]false [B]true
//(2)コンストラクタ(constructor)として呼び出し
var temp=new test_whatisthis();
// #1:[A]true [B]false
// #2:[A]false [B]true
// #3:[A]false [B]true
// #4:[A]true [B]false
// #5:[A]false [B]true
// #6:[A]false [B]true
まず、「(1)関数として呼び出し」た場合には、"test_whatisthis"内でのthisはGlobalオブジェクト("window")になります。
この前提で、
- #1 →"whatisthis"が特定のオブジェクトのプロパティとして扱われていないため、"whatisthis"内のthisはルール2に基づいてGlobalオブジェクト("window")。
- #2 →"test_whatisthis"内での"this"オブジェクト(即ちGlobalオブジェクト)のプロパティとして呼び出されているため、"whatisthis"内のthisはGlobalオブジェクト("window")。
- #3 →'whatisthis.call(<オブジェクト>,<引数>)'は、『"whatisthis"を<オブジェクト>(=this=window)のプロパティとして<引数>(=this=window)を指定して呼び出す』という意味になるため、"whatisthis"内のthisはGlobalオブジェクト("window")。
- #4 →"whatisthis"が特定のオブジェクトのプロパティとして扱われていないため、"whatisthis"内のthisはルール2に基づいてGlobalオブジェクト("window")。
- #5 →"obj"オブジェクトのプロパティとして呼び出されているため、"whatisthis"内のthisは"obj"オブジェクト。
- #6 →'whatisthis.call(<オブジェクト>,<引数>)'は、『"whatisthis"を<オブジェクト>(=obj)のプロパティとして<引数>(=obj)を指定して呼び出す』という意味になるため、"whatisthis"内のthisは"obj"オブジェクト。
この前提で、
- #1 →"whatisthis"が特定のオブジェクトのプロパティとして扱われていないため、"whatisthis"内のthisはルール2に基づいてGlobalオブジェクト("window")。
- #2 →"test_whatisthis"内での"this"オブジェクト(即ち"temp"オブジェクト)のプロパティとして呼び出されているため、"whatisthis"内のthisは"temp"オブジェクト。
- #3 →'whatisthis.call(<オブジェクト>,<引数>)'は、『"whatisthis"を<オブジェクト>(=this=temp)のプロパティとして<引数>(=this=temp)を指定して呼び出す』という意味になるため、"whatisthis"内のthisは"temp"オブジェクト。
- #4 →"whatisthis"が特定のオブジェクトのプロパティとして扱われていないため、"whatisthis"内のthisはルール2に基づいてGlobalオブジェクト("window")。
- #5 →"obj"オブジェクトのプロパティとして呼び出されているため、"whatisthis"内のthisは"obj"オブジェクト。
- #6 →'whatisthis.call(<オブジェクト>,<引数>)'は、『"whatisthis"を<オブジェクト>(=obj)のプロパティとして<引数>(=obj)を指定して呼び出す』という意味になるため、"whatisthis"内のthisは"obj"オブジェクト。
……と、大体説明出来るかなぁ?と思っていましたが、test_whatisthis()の(1)-#2の結果が、IE6では"[A]false [B]false"となってしまい、混乱してしまいました。
上記説明の通り、whatisthisはGlobalオブジェクト("window")のプロパティとして呼び出されており、また引数も"window"になることから、"[A]true [B]true "でないとおかしいはずです(実際に、FirefoxやOperaではこうなります)。
ところがIE6でも、(1)-#2のときのwhatisthis関数内における"this"の持つプロパティを調べてみると、"window"オブジェクトが持っているプロパティと一致するんですね。
とすると……window.whatisthis()が呼び出された瞬間、"window"のコピーが作成され、これが"this"として扱われている???
上記説明の通り、whatisthisはGlobalオブジェクト("window")のプロパティとして呼び出されており、また引数も"window"になることから、"[A]true [B]true "でないとおかしいはずです(実際に、FirefoxやOperaではこうなります)。
ところがIE6でも、(1)-#2のときのwhatisthis関数内における"this"の持つプロパティを調べてみると、"window"オブジェクトが持っているプロパティと一致するんですね。
とすると……window.whatisthis()が呼び出された瞬間、"window"のコピーが作成され、これが"this"として扱われている???
このIE6での現象をもっと単純に再現すると、
これが、window.whatisthisをvar whatisthisに置換えたら、想定通りの動きとなる不思議。
window.whatisthis=function (obj) {
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
};
window.whatisthis(window); // IE6:[A]false [B]false vs others:[A]true [B]true
となります。alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
};
window.whatisthis(window); // IE6:[A]false [B]false vs others:[A]true [B]true
これが、window.whatisthisをvar whatisthisに置換えたら、想定通りの動きとなる不思議。
var whatisthis=function (obj) {
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
};
window.whatisthis(window); // [A]true [B]true
alert('[A](this===window)→'+(this===window)+' [B](this===obj)→'+(this===obj));
};
window.whatisthis(window); // [A]true [B]true
要はwindow.<関数名>=function(){...};という定義方法だと変な動きになる?これまた、ややこしいバグの温床となりそうな現象だ……。
« 【JavaScript】function定義方法による動作の差異(覚書) | トップページ | ひとまず今年の出張期間終了 »
「パソコン・インターネット」カテゴリの記事
- Twitter 原寸びゅー:PC版ブラウザ用・Twitterの画像閲覧と保存がはかどる拡張機能の紹介(2016.02.12)
- スマートフォンをPC上の音楽を再生するためのリモコンとして使いたい(2016.01.10)
- BIGLOBE光ネクスト(大阪)の通信速度問題 - プロバイダ選びは難しい……(2015.08.13)
- BOOK☆WALKER さんに関して最近経験した不安と不満(2015.08.10)
- 『#鳥獣戯画制作キット』が楽しい(2015.07.01)
「覚書」カテゴリの記事
- 鍛高譚 ~ カレイにまつわる物語(2018.05.25)
- ココログをTwitterカードに対応させてみる(2016.11.23)
- 神使の兎 ~宇治神社にて~(2016.07.10)
- Twitter 原寸びゅー:PC版ブラウザ用・Twitterの画像閲覧と保存がはかどる拡張機能の紹介(2016.02.12)
- スマートフォンをPC上の音楽を再生するためのリモコンとして使いたい(2016.01.10)
« 【JavaScript】function定義方法による動作の差異(覚書) | トップページ | ひとまず今年の出張期間終了 »
コメント