この記事は編集レビューを必要としています。ぜひご協力ください。
この翻訳は不完全です。英語から この記事を翻訳 してください。
関数の 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の値を渡すには、callまたはapplyを使用します。
(function(){
'use strict';
function f3(){
return this;
}
f3.call(this) === window; // global object
function f4(){
this.herp = "derp";
}
function Thing(){
this.thisIsEasyToUnderstand = "just kidding";
f4.call(this);
}
var thing = new Thing();
// thing = { thisIsEasyToUnderstand : "just kidding", herp: "derp" };
})();
アロー関数
アロー関数 では、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
thisin different contexts - Gentle explanation of 'this' keyword in JavaScript