this
JavaScriptでの関数のthis
キーワードは、他の言語と比べて少し異なる振る舞いをします。
また、strictモードと非strictモード間でも幾つか違いがあります。
ほとんどの場合、thisの値はどのように関数が呼ばれたかによって決定されます。 実行中の割り当てによって設定することは出来ず、 関数の呼び出しごとに値が異なる可能性があります。 ES(ECMA Script)5で実装されたbindメソッドは、 関数がどのように呼び出されたかに関係なく関数のthis値を設定します。
文法
this
グローバルコンテキスト
グローバル実行コンテキスト内(関数の外)では、 strictモードであるか否かに関わらず、thisはグローバルオブジェクトを参照します。
console.log(this.document === document); // true
// Webブラウザでは、windowオブジェクトは、
// グローバルオブジェクトでもあります。
console.log(this === window); // true
this.a = 37;
console.log(window.a); // 37
関数コンテキスト
関数内では、thisの値は関数の呼び出し方によって変わります。
単純な呼び出し
function f1(){
return this;
}
f1() === window; // グローバルオブジェクト
この場合、thisの値は呼び出しによって設定されません。 このコードはstrictモードでは無いため、 thisの値は常にデフォルトであるグローバルオブジェクトになります。
function f2(){
"use strict"; // strictモードを参照
return this;
}
f2() === undefined;
strictモードでは、thisの値は実行コンテキストに入った時の何かしら設定された値のままです。
もし、何も定義されていなければ、undefined
のままになります。
これには、null
、42
、"I am not this"
のような様々な値を設定可能です。
注意: 2つ目の例では、f2は何かしらの土台無し(例: window.f2())に実行されているため、this値はundefinedのはずです。 この機能は、strictモードのサポートが最初にサポートされた際に、 幾つかのブラウザでは実装されていませんでした。 そのため、適切ではないwindowオブジェクトを返します。
オブジェクトのメソッドとして
関数がオブジェクトのメソッドとして呼び出された場合、 そのthis値には、そのメソッドを呼び出したオブジェクトが設定されます。
下記の例は、o.f()
が実行された際に、
関数内部のthisはそのo
オブジェクトにバインドされます。
var o = {
prop: 37,
f: function() {
return this.prop;
}
};
console.log(o.f()); // 37を出力
thisの振る舞いは、関数がどのようにまたは何処で定義されたかに影響されないことに注意してください。
上記の例は、o
の定義中にf
メンバとして内部関数を定義しています。
ただし、最初に単に関数だけを定義して、後からo.f
を割り当てることも出来ます。
下記の例は、結果的に同じ動作をします。
var o = {prop: 37};
function independent() {
return this.prop;
}
o.f = independent;
console.log(o.f()); // 37を出力
このデモで重要なことは、関数はo
のf
メンバーから実行されているという事です。
同様に、this
のバインドは、直近のメンバー参照によってのみ影響を受けます。
下記は、オブジェクトo.b
のメソッドg
として、
関数を実行している例になります。
これが実行されている際に、関数内部のthis
はo.b
を参照します。
そのオブジェクト自身がo
のメンバーであるという事実は、
何の影響も及ぼしません。
直近の参照にとっては、それが全てです。
o.b = {g: independent, prop: 42};
console.log(o.b.g()); // 42を出力
オブジェクトのprototypeチェーン上でのthis
これまでに説明したthisの概念は、オブジェクトのprototypeチェーン上の何処にメソッドが定義されても同様です。
メソッドがオブジェクトのprototypeチェーン上にある場合、
this
はメソッドを呼び出したオブジェクトを参照します。
var o = {f:function(){ return this.a + this.b; }};
var p = Object.create(o);
p.a = 1;
p.b = 4;
console.log(p.f()); // 5
この例では、変数p
に割り当てられたオブジェクトは、
自身のf
プロパティを持たず、prototypeからそれを継承します。
ただし、f
のために、
o
の名前を持つメンバーが最終的に見つけ出されることは重要ではありません。
p.f
として参照しようとすると、
関数内部のthis
はp
を参照した上で、オブジェクトの値を取得します。
f
はp
のメソッドとして呼び出されるため、
そのthis
はp
を参照します。
これは、JavaScriptのプロトタイプ(prototype)継承の興味深い機能です。
thisによるgetterとsetter
繰り返しになりますが、これまでに説明したthisの概念は、関数がgetterまたはsetterから実行されても同様です。
getterまたはsetterとして使用される関数は、
そのプロパティの設定元または取得元から、
そのオブジェクトへバインドされたthis
を持ちます。
function modulus(){
return Math.sqrt(this.re * this.re + this.im * this.im);
}
var o = {
re: 1,
im: -1,
get phase(){
return Math.atan2(this.im, this.re);
}
};
Object.defineProperty(o, 'modulus', {
get: modulus,
enumerable:true,
configurable:true
});
console.log(o.phase, o.modulus); // -0.78 1.4142 を出力
コンストラクタで使用
関数がコンストラクタ(newキーワードと一緒に)として使用される場合、
そのthis
はコンストラクタ(構築)される新しいオブジェクトにバインドされます。
注意: デフォルトではコンストラクタはthis
によって参照されるオブジェクトを返しますが、
代わりに別のオブジェクトを返すことも可能です。
(もし返される値がオブジェクトで無ければ、this
によって参照されるオブジェクトが返されます)
/*
* コンストラクタは次のように動作します:
*
* function MyConstructor(){
* // 実際の関数本文のコードはここに書きます。
* // 割り当てたいプロパティをthis上に作成します。
* // 例えば、
* this.fum = "nom";
* // その他...
*
* // この関数がオブジェクトを返す文を持つ場合、
* // そのオブジェクトはnew式の結果になります。
* // そうでなければ、new式の結果は、そのオブジェクトに
* // 現在バインドしているthisになります。
* // (一般的に見受けられるのは、ほとんどがこのケースです。)
* }
*/
function C(){
this.a = 37;
}
var o = new C();
console.log(o.a); // 37出力
function C2(){
this.a = 37;
return {a:38};
}
o = new C2();
console.log(o.a); // 38出力
最後の例(C2)では、オブジェクトがコンストラクタ中に返されているため、
this
がバインドされた新しいオブジェクトは単純に破棄されます。
(これは、実質的に"this.a = 37;"
の文を排除されたコード(dead code)にしています。
正確には実行されるため、排除されているわけでありませんが、
外から干渉できないため実質的にそうなっていると言えます。)
callとapply
関数がthis
キーワードをその本文内で使用する場合、
そのthis
の値を呼び出しする際に、
Function.prototype
から全ての関数に継承されるcallまたはapplyメソッドを使用することで、
特定のオブジェクトにバインドすることが可能です。
function add(c, d){
return this.a + this.b + c + d;
}
var o = {a:1, b:3};
// 1つ目の引数は'this'として使用するオブジェクト、
// その後に続く引数は関数呼び出しに引数として
// 渡されるものです。
add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16
// 1つ目の引数は'this'として使用するオブジェクト、
// 2つ目の引数は関数呼び出しに引数として使用される要素を持つ
// 配列になります。
add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
callとapplyの呼び出しで、
もしthis
として渡す値がオブジェクトでは無い場合、
内部のToObject
オペレーションを使用して、
オブジェクトへの変換が試みまれる事に注意してください。
そのため、7や'foo'のようなプリミティブな値が渡されると
関連するコンストラクタを使用して変換が行われ、
プリミティブの数値7は、まるでnew Number(7)
によって作られたオブジェクトであるように変換され、
文字列'foo'はまるでnew String('foo')
によって作られたオブジェクトであるように変換されます。
下記は実際の例になります。
function bar() {
console.log(Object.prototype.toString.call(this));
}
bar.call(7); // [object Number]
bindメソッドについて
ECMAScript 5でFunction.prototype.bindが導入されました。
f.bind(someObject)
の呼び出しは、f
と同じ本文、同じスコープの新しい関数を作成しますが、
関数がどのように使用されるかに関係なく、
bind
の1つ目の引数が、新しい関数のthis
に永続的にバインドされます。
function f(){
return this.a;
}
//fから、bindを使用して新しい関数gを作成
var g = f.bind({a:"azerty"});
console.log(g()); // azerty
var o = {a:37, f:f, g:g};
console.log(o.f(), o.g()); // 37, azerty
DOMイベントハンドラとしての使用
関数がイベントハンドラとして使用される場合、
そのthis
にはイベント発火元が設定されます。
(一部のブラウザがこの規則に従わず、そういった場合は、
addEventListener以外のメソッドで直接リスナーを追加します。)
// リスナーとして呼び出された場合、関係する要素を青色にします。
function bluify(e){
// 常にtrue
console.log(this === e.currentTarget);
// currentTargetとtargetが同じオブジェクトであれば、true
console.log(this === e.target);
this.style.backgroundColor = '#A5D9F3'; //青色
}
// document内の各要素のリストを取得
var elements = document.getElementsByTagName('*');
// クリックリスナーとしてbluifyを追加するため、
// 要素がクリックすると青色になります。
for(var i=0 ; i<elements.length ; i++){
elements[i].addEventListener('click', bluify, false);
}
インラインのイベントハンドラでの使用
インラインのハンドラからコードが呼び出された場合、
そのthis
には、そのリスナー(onclick)が置かれたDOM要素が設定されます。
<button onclick="alert(this.tagName.toLowerCase());">
Show this
</button>
上記は"button"という文字列をアラートで表示します。
ただし、この外部コードだけが、この方法でそのthis
を持つことに注意してください。
<button onclick="alert((function(){return this}()));">
Show inner this
</button>
上記のケースでは、関数内部のthis
は設定されないため、グローバル/windowオブジェクトを返します。
(つまり、非strictモードのデフォルトのオブジェクトのthisは、その呼び出しによって設定されません。)
仕様
ブラウザ互換性
機能 | Chrome | Firefox (Gecko) |
IE | Opera | Safari |
---|---|---|---|---|---|
基本 | ◯ | ◯ | ◯ | ◯ | ◯ |
機能 | Android | Chrome for Android |
Firefox Mobile |
IE Mobile |
Opera Mobile |
Safari Mobile |
---|---|---|---|---|---|---|
基本 | ◯ | ◯ | ◯ | ◯ | ◯ | ◯ |
関連項目
© 2017 Mozilla Contributors
Licensed under the Creative Commons Attribution-ShareAlike License v2.5 or later.
このページは、ページトップのURL先のMozilla Developer Network(以下、MDN)のコンテンツを翻訳した内容を基に構成されています。 構成について異なる点も含まれますので、下記の項目を確認し、必要に応じて元のコンテンツをご確認ください。 もし、誤訳などの間違いを見つけましたら、 @tomofまで教えていただければ幸いです。
- 特定のブラウザに特化しすぎている情報やあまりにも古い情報、 または試験的に導入されているようなAPIや機能については、省略していることがあります。
- 例やデモについて、実際にページ内で動作させる関係で一部ソースコードを変更している場合や、 その例で使用しているコンテンツの単語や文章などを日本人向けに変更しいてる場合があります。
- MDNの更新頻度が高いため、元のコンテンツと比べ情報が古くなっている可能性があります。
- "訳注:"などの断わりを入れた上で、日本人向けの情報の追記を行っている事があります。