この記事は編集レビューを必要としています。ぜひご協力ください。
関数の this
キーワード は、JavaScript ではほかの言語と少々異なる動作をします。また、strict モード と非 strict モードでも違いがあります。
ほとんどの場合、this
の値は、関数の呼ばれ方によって決定されます。これは実行時に割り当てできず、関数が呼び出されるたびに異なる可能性があります。ES5 で 呼び出し方にかかわらず関数の this
の値を設定するために、bind
メソッドが導入され、ECMAScript 2015 で this
がレキシカルスコープである(囲まれている実行コンテクストの 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"
のような何らかの値を設定することもできます。
f2
は直接呼び出されており、オブジェクト(たとえば、window.f2()
)のメソッドやプロパティではないため、this
は undefined
です。strict モード が初めてサポートされ始めたとき、いくつかのブラウザではこの機能が実装されませんでした。結果的に、それらのブラウザは不正確にも window
オブジェクトを返します。アロー関数
アロー関数 では、this
はレキシカルに設定されます。すなわち、 それを囲む実行コンテキストの this
の値が設定されます。グローバルコードでは、グローバルオブジェクトが設定されます:
var globalObject = this; var foo = (() => this); console.log(foo() === globalObject); // true
foo
の呼び出され方にかかわらず、this
はグローバルオブジェクトであり続けます。オブジェクトのメソッド(通常は this
にオブジェクトが設定される)として、call
や apply
、bind
で呼び出した場合でも、値を保ちます:
// オブジェクトのメソッドとして呼び出す。 var obj = {foo: foo}; console.log(obj.foo() === globalObject); // true // call を使用して this の設定を試みる。 console.log(foo.call(obj) === globalObject); // true // bind を使用して this の設定を試みる。 foo = foo.bind(obj); console.log(foo() === globalObject); // true
何があっても、foo
の this
は生成されたときの値が設定されています(上記の例では、グローバルオブジェクトです)。同様のことが、ほかの関数内で生成したアロー関数にも適用されます:それらの this
には、外部の実行コンテクストのものが設定されます。
// this を返す 関数を返す bar メソッドを持つ obj を生成します。 // 返された関数はアロー関数として生成されているため、その this は、 // 永続的にその囲まれた関数の this に拘束されます。 // bar の値は呼び出し時に設定でき、戻り値の関数の値に順に設定します。 var obj = { bar : function() { var x = (() => this); return x; } }; // obj のメソッドとして呼び出し、その this を obj に設定します。 // 戻り値の関数への参照を fn に割り当てます。 var fn = obj.bar(); // strict モードでは、this を設定せずに fn を呼び出すと // 通常はグローバルオブジェクトか undefined が既定値となります。 console.log(fn() === obj); // true
上記では、関数(この匿名関数を A と呼びます)に obj.bar
が返すアロー関数として生成されたほかの関数(この匿名関数を B と呼びます)を割り当てています。結果として、呼び出されたときに関数 B の this
は、永続的に obj.bar
(関数 A) の this
が設定されます。返された関数(関数 B)が呼びされるとき、その this
は常に最初に設定されたものになります。上記のコード例では、関数 B の this
は obj である関数 A の this
が設定されているため、通常はその this
に undefined
かグローバルオブジェクト(または、以前の例のグローバルコンテキストのように、いずれかのメソッド)が設定されますが、 obj
の設定が残ります。
オブジェクトのメソッドとして
関数がオブジェクトのメソッドとして呼び出されるとき、その this
にはメソッドが呼び出されたオブジェクトが設定されます。
次の例では、o.f()
が起動したとき、関数内の this
には、o
オブジェクトが関連付けられます。
var o = { prop: 37, f: function() { return this.prop; } }; console.log(o.f()); // logs 37
この振る舞いは、関数定義の方法や場所に全く影響を受けないことに注意してください。前述の例では、o
の定義中に f
メンバーとして関数をインラインに定義しています。しかし、関数を最初に定義して、後から o.f
に付け足すことができます。その結果は同じ振る舞いになります:
var o = {prop: 37}; function independent() { return this.prop; } o.f = independent; console.log(o.f()); // logs 37
これは、関数が o
の f
のメンバーとして呼び出されることだけが重要なことを示しています。
同様に、this
のバインディングは、最直近のメンバー参照にのみ影響を受けます。次の例では、関数が呼び出すとき、オブジェクト o.b
の g
メソッドとして呼び出しています。実行時に、関数内の this
は o.b
を参照します。オブジェクト自体が o
のメンバーであるという事実は何の意味もありません:最直近の参照のみが重要なのです。
o.b = {g: independent, prop: 42}; console.log(o.b.g()); // logs 42
オブジェクトのプロトタイプチェーン上の this
同じ概念が、オブジェクトのプロトタイプチェーンのどこかに定義されたメソッドにも当てはまります。メソッドがオブジェクトのプロトタイプチェーン上にあった場合、メソッドがオブジェクト上にあるかのように、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
プロパティを持たず、プロトタイプから継承しています。しかし、f
に対する検索が、最終的に o
でその名前を持つメンバーを見つけることは重要ではありません; 検索は p.f
への参照から開始されるため、関数内の this
は p
として参照されるオブジェクトの値を取ります。f
は p
のメソッドとして呼ばれたため、その this
は p
を参照します。これは、JavaScript のプロトタイプ継承の興味深い機能です。
ゲッター/セッターと this
再度、同じ概念が、ゲッターやセッターから呼ばれる関数にも当てはまります。ゲッターやセッターとして使用される関数は、このプロパティを設定するか、または得られている元のオブジェクトにバインドされている this
を持ちます。
function sum(){ return this.a + this.b + this.c; } var o = { a: 1, b: 2, c: 3, get average(){ return (this.a + this.b + this.c) / 3; } }; Object.defineProperty(o, 'sum', { get: sum, enumerable:true, configurable:true}); console.log(o.average, o.sum); // logs 2, 6
コンストラクタとして
関数がコンストラクタとして(new
キーワードとともに)使用されるとき、その this
は生成された新しいオブジェクトにバインドされます。
コンストラクタの既定では、this
で参照されるオブジェクトを返しますが、代わりにほかのオブジェクトを返すことができます(戻り値がオブジェクトではない場合、this
オブジェクトが返されます)。
/* * Constructors work like this: * * function MyConstructor(){ * // Actual function body code goes here. * // Create properties on |this| as * // desired by assigning to them. E.g., * this.fum = "nom"; * // et cetera... * * // If the function has a return statement that * // returns an object, that object will be the * // result of the |new| expression. Otherwise, * // the result of the expression is the object * // currently bound to |this| * // (i.e., the common case most usually seen). * } */ function C(){ this.a = 37; } var o = new C(); console.log(o.a); // logs 37 function C2(){ this.a = 37; return {a:38}; } o = new C2(); console.log(o.a); // logs 38
最後の例(C2
)では、オブジェクトが構築中に返されたので、this
がバインドされている新しいオブジェクトは単に破棄されています。(これは根本的に "this.a = 37;
" ステートメントを死んだコードにしてしまっています。これは実行されるので、正確には死んだコードではありませんが、外部への影響がありません。)
call と
apply
関数本体に this
キーワードを使用する場合、すべての関数が Function.prototype
から継承する call
メソッドか apply
メソッドを使用した呼び出しで、その値に特定のオブジェクトをバインドできます。
function add(c, d){ return this.a + this.b + c + d; } var o = {a:1, b:3}; // The first parameter is the object to use as // 'this', subsequent parameters are passed as // arguments in the function call add.call(o, 5, 7); // 1 + 3 + 5 + 7 = 16 // The first parameter is the object to use as // 'this', the second is an array whose // members are used as the arguments in the function call add.apply(o, [10, 20]); // 1 + 3 + 10 + 20 = 34
call
と apply
は、this
として渡された値がオブジェクトではない場合、内部の ToObject
操作を使用してオブジェクトに変換しようと試みることに注意してください。7
や 'foo'
のようなプリミティブが渡された場合、関連するコンストラクタを使用してオブジェクトに変換されます。たとえば、プリミティブの number の 7
は new Number(7)
による場合のようにオブジェクトに変換され、string の '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
と同じ内部とスコープを持つ新しい関数を生成し、ここが this
が発生するオリジナルの関数ですが、関数がどのように使われるかにかかわらず、新しい関数では bind
の最初の引数に永続的にバインドされます。
function f(){ return this.a; } 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
メソッド以外では動的にリスナを追加する規則に従いません)。
// When called as a listener, turns the related element blue function bluify(e){ // Always true console.log(this === e.currentTarget); // true when currentTarget and target are the same object console.log(this === e.target); this.style.backgroundColor = '#A5D9F3'; } // Get a list of every element in the document var elements = document.getElementsByTagName('*'); // Add bluify as a click listener so when the // element is clicked on, it turns blue for(var i=0 ; i<elements.length ; i++){ elements[i].addEventListener('click', bluify, false); }
インラインイベントハンドラ内
コードがインライン on-event handler から呼ばれたとき、その this
にはリスナが配置されている DOM 要素が設定されます:
<button onclick="alert(this.tagName.toLowerCase());"> Show this </button>
上記の alert は button
を表示します。外側のコードがこのように設定された this
を持っているだけだということに注意してください。
<button onclick="alert((function(){return this})());"> Show inner this </button>
この場合、内側の関数の this
は設定されていないので、グローバルか window オブジェクトを返します(つまり、this
が呼び出しによって設定されていないので、非 strict モードの既定オブジェクトです)。
仕様
ブラウザ実装状況
機能 | Chrome | Firefox (Gecko) | Internet Explorer | Opera | Safari |
---|---|---|---|---|---|
基本サポート | (有) | (有) | (有) | (有) | (有) |
機能 | Android | Chrome for Android | Firefox Mobile (Gecko) | IE Mobile | Opera Mobile | Safari Mobile |
---|---|---|---|---|---|---|
基本サポート | (有) | (有) | (有) | (有) | (有) | (有) |
関連項目
- Strict モード
- All this, an article about
this
in different contexts - Gentle explanation of 'this' keyword in JavaScript