概要: このチュートリアルでは、JavaScriptのthis
の値について学習し、さまざまなコンテキストで明確に理解します。
Java、C#、またはPHPなどの他のプログラミング言語を使用したことがある場合は、すでにthis
キーワードに精通しているでしょう。
これらの言語では、this
キーワードはクラスの現在のインスタンスを表し、クラス内でのみ意味を持ちます。
JavaScriptにもthis
キーワードがあります。ただし、JavaScriptのthis
キーワードは、他のプログラミング言語とは異なる動作をします。
JavaScriptでは、グローバルコンテキストと関数コンテキストでthis
キーワードを使用できます。さらに、this
キーワードの動作は、厳格モードと非厳格モードで異なります。
thisキーワードとは
一般的に、this
は、関数がプロパティであるオブジェクトを参照します。言い換えれば、this
は現在関数を呼び出しているオブジェクトを参照します。
next()
メソッドを持つcounter
オブジェクトがあるとします。next()
メソッドを呼び出すと、this
オブジェクトにアクセスできます。
let counter = {
count: 0,
next: function () {
return ++this.count;
},
};
counter.next();
Code language: JavaScript (javascript)
next()
関数内では、this
はcounter
オブジェクトを参照します。次のメソッド呼び出しを参照してください。
counter.next();
Code language: CSS (css)
next()
は、counter
オブジェクトのプロパティである関数です。したがって、next()
関数内では、this
はcounter
オブジェクトを参照します。
グローバルコンテキスト
グローバルコンテキストでは、this
はグローバルオブジェクトを参照します。これは、Webブラウザではwindow
オブジェクト、Node.jsではglobal
オブジェクトです。
この動作は、厳格モードと非厳格モードの両方で一貫しています。以下は、Webブラウザでの出力です。
console.log(this === window); // true
Code language: JavaScript (javascript)
グローバルコンテキストでthis
オブジェクトにプロパティを割り当てると、JavaScriptは次の例に示すように、グローバルオブジェクトにプロパティを追加します。
this.color= 'Red';
console.log(window.color); // 'Red'
Code language: JavaScript (javascript)
関数コンテキスト
JavaScriptでは、関数を次の方法で呼び出すことができます。
- 関数呼び出し
- メソッド呼び出し
- コンストラクタ呼び出し
- 間接呼び出し
各関数呼び出しは、独自のコンテキストを定義します。したがって、this
は異なる動作をします。
1)単純な関数呼び出し
非厳格モードでは、関数が次のように呼び出された場合、this
はグローバルオブジェクトを参照します。
function show() {
console.log(this === window); // true
}
show();
Code language: JavaScript (javascript)
show()
関数を呼び出すと、this
はグローバルオブジェクトを参照します。これは、Webブラウザではwindow
、Node.jsではglobal
です。
show()
関数を呼び出すことは、次と同じです。
window.show();
Code language: JavaScript (javascript)
厳格モードでは、JavaScriptは関数内のthis
をundefined
に設定します。例:
"use strict";
function show() {
console.log(this === undefined);
}
show();
Code language: JavaScript (javascript)
厳格モードを有効にするには、JavaScriptファイルの先頭で"use strict"
ディレクティブを使用します。厳格モードを特定の関数のみに適用する場合は、関数本体の先頭に配置します。
厳格モードはECMAScript 5.1以降で使用可能になっていることに注意してください。strict
モードは、関数とネストされた関数の両方に適用されます。例:
function show() {
"use strict";
console.log(this === undefined); // true
function display() {
console.log(this === undefined); // true
}
display();
}
show();
Code language: JavaScript (javascript)
出力
true
true
Code language: JavaScript (javascript)
コンソールに示すように、display()
内部関数では、this
もundefined
に設定されます。
2)メソッド呼び出し
オブジェクトのメソッドを呼び出すと、JavaScriptはthis
をメソッドを所有するオブジェクトに設定します。次のcar
オブジェクトを参照してください。
let car = {
brand: 'Honda',
getBrand: function () {
return this.brand;
}
}
console.log(car.getBrand()); // Honda
Code language: JavaScript (javascript)
この例では、getBrand()
メソッドのthis
オブジェクトはcar
オブジェクトを参照します。
メソッドは値であるオブジェクトのプロパティであるため、変数に格納できます。
let brand = car.getBrand;
Code language: JavaScript (javascript)
次に、変数を介してメソッドを呼び出します。
console.log(brand()); // undefined
Code language: JavaScript (javascript)
オブジェクトを指定せずにメソッドを呼び出すと、JavaScriptは非厳格モードでthis
をグローバルオブジェクトに、厳格モードでundefined
に設定するため、"Honda"
の代わりにundefined
が返されます。
この問題を解決するには、Function.prototype
オブジェクトのbind()
メソッドを使用します。bind()
メソッドは、this
キーワードが指定された値に設定された新しい関数を作成します。
let brand = car.getBrand.bind(car);
console.log(brand()); // Honda
Code language: JavaScript (javascript)
この例では、brand()
メソッドを呼び出すと、this
キーワードがcar
オブジェクトにバインドされます。例:
let car = {
brand: 'Honda',
getBrand: function () {
return this.brand;
}
}
let bike = {
brand: 'Harley Davidson'
}
let brand = car.getBrand.bind(bike);
console.log(brand());
Code language: JavaScript (javascript)
出力
Harley Davidson
この例では、bind()
メソッドがthis
をbike
オブジェクトに設定するため、コンソールにbike
オブジェクトのbrand
プロパティの値が表示されます。
3)コンストラクタ呼び出し
new
キーワードを使用して関数オブジェクトのインスタンスを作成する場合、関数をコンストラクタとして使用します。
次の例では、Car
関数を宣言し、コンストラクタとして呼び出します。
function Car(brand) {
this.brand = brand;
}
Car.prototype.getBrand = function () {
return this.brand;
}
let car = new Car('Honda');
console.log(car.getBrand());
Code language: JavaScript (javascript)
式new Car('Honda')
は、Car
関数のコンストラクタ呼び出しです。
JavaScriptは新しいオブジェクトを作成し、this
を新しく作成されたオブジェクトに設定します。このパターンは、1つの潜在的な問題を除いてうまく機能します。
ここで、Car()
を関数またはコンストラクタとして呼び出すことができます。次のようにnew
キーワードを省略すると、
var bmw = Car('BMW');
console.log(bmw.brand);
// => TypeError: Cannot read property 'brand' of undefined
Code language: JavaScript (javascript)
Car()
のthis
の値がグローバルオブジェクトに設定されるため、bmw.brand
はundefined
を返します。
Car()
関数が常にコンストラクタ呼び出しを使用して呼び出されるようにするには、次のようにCar()
関数の先頭にチェックを追加します。
function Car(brand) {
if (!(this instanceof Car)) {
throw Error('Must use the new operator to call the function');
}
this.brand = brand;
}
Code language: JavaScript (javascript)
ES6では、関数が単純な呼び出しとして呼び出されたか、コンストラクタとして呼び出されたかを検出できる、new.target
という名前のメタプロパティが導入されました。
次のように、new.target
メタプロパティを使用するCar()
関数を変更できます。
function Car(brand) {
if (!new.target) {
throw Error('Must use the new operator to call the function');
}
this.brand = brand;
}
Code language: JavaScript (javascript)
4)間接呼び出し
JavaScriptでは、関数はファーストクラスの市民です。言い換えれば、関数はFunction型のインスタンスであるオブジェクトです。
Function
型には、call()
とapply()
という2つのメソッドがあります。これらのメソッドを使用すると、関数を呼び出すときにthis
値を設定できます。例:
function getBrand(prefix) {
console.log(prefix + this.brand);
}
let honda = {
brand: 'Honda'
};
let audi = {
brand: 'Audi'
};
getBrand.call(honda, "It's a ");
getBrand.call(audi, "It's an ");
Code language: JavaScript (javascript)
出力
It's a Honda
It's an Audi
Code language: PHP (php)
この例では、getBrand()
関数のcall()
メソッドを使用して、getBrand()
関数を間接的に呼び出しました。call()
メソッドの最初の引数としてhonda
とaudi
オブジェクトを渡したため、各呼び出しで対応するブランドを取得しました。
apply()
メソッドは、2番目の引数が引数の配列である点を除いて、call()
メソッドに似ています。
getBrand.apply(honda, ["It's a "]); // "It's a Honda"
getBrand.apply(audi, ["It's an "]); // "It's a Audi"
Code language: JavaScript (javascript)
アロー関数
ES6では、アロー関数と呼ばれる新しい概念が導入されました。アロー関数では、JavaScriptはthis
を字句的に設定します。
つまり、アロー関数は独自の実行コンテキストを作成せず、アロー関数が定義されている外側の関数からthis
を継承します。次の例を参照してください。
let getThis = () => this;
console.log(getThis() === window); // true
Code language: JavaScript (javascript)
この例では、this
の値はグローバルオブジェクト、つまりWebブラウザではwindow
に設定されます。
アロー関数は独自の実行コンテキストを作成しないため、アロー関数を使用してメソッドを定義すると問題が発生します。例:
function Car() {
this.speed = 120;
}
Car.prototype.getSpeed = () => {
return this.speed;
};
var car = new Car();
console.log(car.getSpeed()); // 👉 undefined
Code language: JavaScript (javascript)
getSpeed()
メソッド内では、this
の値はグローバルオブジェクトを参照しますが、Car
オブジェクトではありません。ただし、グローバルオブジェクトにはspeedというプロパティがないため、getSpeed()
メソッドのthis.speed
はundefined
を返します。