this
異なっJavaScriptでキーワード振る舞いは他の言語と比較します。オブジェクト指向言語では、this
キーワードはクラスの現在のインスタンスを指します。JavaScriptでは、の値はthis
関数(context.function()
)の呼び出しコンテキストとそれが呼び出される場所によって決まります。
1.グローバルコンテキストで使用する場合
this
グローバルコンテキストで使用すると、(window
ブラウザで)グローバルオブジェクトにバインドされます
document.write(this); //[object Window]
this
グローバルコンテキストで定義された関数の内部で使用するとthis
、関数は実際にはグローバルコンテキストのメソッドになるため、は引き続きグローバルオブジェクトにバインドされます。
function f1()
{
return this;
}
document.write(f1()); //[object Window]
上記f1
はグローバルオブジェクトのメソッドです。したがってwindow
、次のようにオブジェクトで呼び出すこともできます。
function f()
{
return this;
}
document.write(window.f()); //[object Window]
2.オブジェクトメソッド内で使用する場合
this
オブジェクトメソッド内でキーワードを使用するthis
と、「即時」の囲みオブジェクトにバインドされます。
var obj = {
name: "obj",
f: function () {
return this + ":" + this.name;
}
};
document.write(obj.f()); //[object Object]:obj
上記では、即時という単語を二重引用符で囲んでいます。これは、オブジェクトを別のオブジェクト内にネストするthis
と、直接の親にバインドされるということです。
var obj = {
name: "obj1",
nestedobj: {
name:"nestedobj",
f: function () {
return this + ":" + this.name;
}
}
}
document.write(obj.nestedobj.f()); //[object Object]:nestedobj
メソッドとしてオブジェクトに関数を明示的に追加した場合でも、上記のルールに従いthis
ます。つまり、直接の親オブジェクトを指します。
var obj1 = {
name: "obj1",
}
function returnName() {
return this + ":" + this.name;
}
obj1.f = returnName; //add method to object
document.write(obj1.f()); //[object Object]:obj1
3.コンテキストレス関数を呼び出すとき
this
コンテキストなしで(つまり、オブジェクト上ではなく)呼び出される関数内を使用すると、(window
ブラウザ内で)グローバルオブジェクトにバインドされます(関数がオブジェクト内で定義されている場合でも)。
var context = "global";
var obj = {
context: "object",
method: function () {
function f() {
var context = "function";
return this + ":" +this.context;
};
return f(); //invoked without context
}
};
document.write(obj.method()); //[object Window]:global
関数ですべてを試す
上記のポイントも関数で試すことができます。ただし、いくつかの違いがあります。
- 上記では、オブジェクトリテラル表記を使用してオブジェクトにメンバーを追加しました。を使用して、関数にメンバーを追加でき
this
ます。それらを指定します。
- オブジェクトリテラル表記は、すぐに使用できるオブジェクトのインスタンスを作成します。関数では、最初に
new
演算子を使用してインスタンスを作成する必要がある場合があります。
- また、オブジェクトリテラルアプローチでは、ドット演算子を使用して、定義済みのオブジェクトにメンバーを明示的に追加できます。これは特定のインスタンスにのみ追加されます。ただし、関数のすべてのインスタンスに反映されるように、関数プロトタイプに変数を追加しました。
以下では、Object this
以上で行ったことをすべて試しましたが、オブジェクトを直接作成する代わりに、まず関数を作成しました。
/*********************************************************************
1. When you add variable to the function using this keyword, it
gets added to the function prototype, thus allowing all function
instances to have their own copy of the variables added.
*********************************************************************/
function functionDef()
{
this.name = "ObjDefinition";
this.getName = function(){
return this+":"+this.name;
}
}
obj1 = new functionDef();
document.write(obj1.getName() + "<br />"); //[object Object]:ObjDefinition
/*********************************************************************
2. Members explicitly added to the function protorype also behave
as above: all function instances have their own copy of the
variable added.
*********************************************************************/
functionDef.prototype.version = 1;
functionDef.prototype.getVersion = function(){
return "v"+this.version; //see how this.version refers to the
//version variable added through
//prototype
}
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
3. Illustrating that the function variables added by both above
ways have their own copies across function instances
*********************************************************************/
functionDef.prototype.incrementVersion = function(){
this.version = this.version + 1;
}
var obj2 = new functionDef();
document.write(obj2.getVersion() + "<br />"); //v1
obj2.incrementVersion(); //incrementing version in obj2
//does not affect obj1 version
document.write(obj2.getVersion() + "<br />"); //v2
document.write(obj1.getVersion() + "<br />"); //v1
/*********************************************************************
4. `this` keyword refers to the immediate parent object. If you
nest the object through function prototype, then `this` inside
object refers to the nested object not the function instance
*********************************************************************/
functionDef.prototype.nestedObj = { name: 'nestedObj',
getName1 : function(){
return this+":"+this.name;
}
};
document.write(obj2.nestedObj.getName1() + "<br />"); //[object Object]:nestedObj
/*********************************************************************
5. If the method is on an object's prototype chain, `this` refers
to the object the method was called on, as if the method was on
the object.
*********************************************************************/
var ProtoObj = { fun: function () { return this.a } };
var obj3 = Object.create(ProtoObj); //creating an object setting ProtoObj
//as its prototype
obj3.a = 999; //adding instance member to obj3
document.write(obj3.fun()+"<br />");//999
//calling obj3.fun() makes
//ProtoObj.fun() to access obj3.a as
//if fun() is defined on obj3
4.コンストラクター関数内で使用する場合。
関数がコンストラクターとして使用される場合(つまり、new
キーワードで呼び出される場合)、this
関数本体の内部は、構築される新しいオブジェクトを指します。
var myname = "global context";
function SimpleFun()
{
this.myname = "simple function";
}
var obj1 = new SimpleFun(); //adds myname to obj1
//1. `new` causes `this` inside the SimpleFun() to point to the
// object being constructed thus adding any member
// created inside SimipleFun() using this.membername to the
// object being constructed
//2. And by default `new` makes function to return newly
// constructed object if no explicit return value is specified
document.write(obj1.myname); //simple function
5.プロトタイプチェーンで定義された関数内で使用する場合
メソッドがオブジェクトのプロトタイプチェーンにある場合、this
そのようなメソッド内では、メソッドがオブジェクトで定義されているかのように、メソッドが呼び出されたオブジェクトを参照します。
var ProtoObj = {
fun: function () {
return this.a;
}
};
//Object.create() creates object with ProtoObj as its
//prototype and assigns it to obj3, thus making fun()
//to be the method on its prototype chain
var obj3 = Object.create(ProtoObj);
obj3.a = 999;
document.write(obj3.fun()); //999
//Notice that fun() is defined on obj3's prototype but
//`this.a` inside fun() retrieves obj3.a
6. call()、apply()およびbind()関数の内部
- これらのメソッドはすべてで定義されてい
Function.prototype
ます。
- これらのメソッドを使用すると、関数を1回記述して別のコンテキストで呼び出すことができます。つまり、
this
関数の実行中に使用される値を指定できます。また、呼び出されたときに元の関数に渡されるすべてのパラメーターを受け取ります。
fun.apply(obj1 [, argsArray])
内部obj1
の値として設定し、その引数として渡す要素を呼び出します。this
fun()
fun()
argsArray
fun.call(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- 内部obj1
の値として設定し、引数として渡す呼び出し。this
fun()
fun()
arg1, arg2, arg3, ...
fun.bind(obj1 [, arg1 [, arg2 [,arg3 [, ...]]]])
- 内部funがバインドされ、指定されたパラメーターにバインドされたパラメーターをfun
持つ関数への参照を返します。this
obj1
fun
arg1, arg2, arg3,...
- 今では
apply
、call
とのbind
違いが明らかになっているはずです。apply
配列のようなオブジェクト、つまり数値length
プロパティと対応する負でない整数プロパティを持つオブジェクトとして機能する引数を指定することができます。一方call
、関数の引数を直接指定できます。両方とも、指定されたコンテキストで、指定された引数を使用して関数apply
をcall
すぐに呼び出します。一方、bind
指定されたthis
値と引数にバインドされた関数を返すだけです。この返された関数への参照を変数に割り当てることでキャプチャでき、後でいつでも呼び出すことができます。
function add(inc1, inc2)
{
return this.a + inc1 + inc2;
}
var o = { a : 4 };
document.write(add.call(o, 5, 6)+"<br />"); //15
//above add.call(o,5,6) sets `this` inside
//add() to `o` and calls add() resulting:
// this.a + inc1 + inc2 =
// `o.a` i.e. 4 + 5 + 6 = 15
document.write(add.apply(o, [5, 6]) + "<br />"); //15
// `o.a` i.e. 4 + 5 + 6 = 15
var g = add.bind(o, 5, 6); //g: `o.a` i.e. 4 + 5 + 6
document.write(g()+"<br />"); //15
var h = add.bind(o, 5); //h: `o.a` i.e. 4 + 5 + ?
document.write(h(6) + "<br />"); //15
// 4 + 5 + 6 = 15
document.write(h() + "<br />"); //NaN
//no parameter is passed to h()
//thus inc2 inside add() is `undefined`
//4 + 5 + undefined = NaN</code>
7. this
内部イベントハンドラー
- 要素のイベントハンドラーに関数を直接割り当てる場合、
this
イベント処理関数内で直接使用すると、対応する要素が参照されます。このような直接的な機能の割り当てはaddeventListener
、メソッドを使用して、またはのような従来のイベント登録メソッドを介して実行できますonclick
。
- 同様に、要素の
this
イベントプロパティ(など<button onclick="...this..." >
)内で直接使用する場合は、要素を参照します。
- ただし
this
、イベント処理関数またはイベントプロパティ内で呼び出された他の関数を介して間接的に使用すると、グローバルオブジェクトに解決されますwindow
。
- 上記の同じ動作は、Microsoftのイベント登録モデルメソッドを使用して関数をイベントハンドラーにアタッチしたときにも実現され
attachEvent
ます。関数をイベントハンドラーに割り当てる(つまり、要素の関数メソッドを作成する)代わりに、イベントで関数を呼び出します(グローバルコンテキストで効果的に呼び出します)。
JSFiddleでこれを試すことをお勧めします。
<script>
function clickedMe() {
alert(this + " : " + this.tagName + " : " + this.id);
}
document.getElementById("button1").addEventListener("click", clickedMe, false);
document.getElementById("button2").onclick = clickedMe;
document.getElementById("button5").attachEvent('onclick', clickedMe);
</script>
<h3>Using `this` "directly" inside event handler or event property</h3>
<button id="button1">click() "assigned" using addEventListner() </button><br />
<button id="button2">click() "assigned" using click() </button><br />
<button id="button3" onclick="alert(this+ ' : ' + this.tagName + ' : ' + this.id);">used `this` directly in click event property</button>
<h3>Using `this` "indirectly" inside event handler or event property</h3>
<button onclick="alert((function(){return this + ' : ' + this.tagName + ' : ' + this.id;})());">`this` used indirectly, inside function <br /> defined & called inside event property</button><br />
<button id="button4" onclick="clickedMe()">`this` used indirectly, inside function <br /> called inside event property</button> <br />
IE only: <button id="button5">click() "attached" using attachEvent() </button>
8. this
ES6の矢印関数
アロー関数でthis
は、一般的な変数のように動作します:字句スコープから継承されます。関数のthis
矢印関数が定義され、矢印機能のだろうthis
。
したがって、これは次と同じ動作です。
(function(){}).bind(this)
次のコードを参照してください。
const globalArrowFunction = () => {
return this;
};
console.log(globalArrowFunction()); //window
const contextObject = {
method1: () => {return this},
method2: function(){
return () => {return this};
}
};
console.log(contextObject.method1()); //window
const contextLessFunction = contextObject.method1;
console.log(contextLessFunction()); //window
console.log(contextObject.method2()()) //contextObject
const innerArrowFunction = contextObject.method2();
console.log(innerArrowFunction()); //contextObject