この記事は、2018年04月05日に限定公開として作成したあと、2024年に全体に公開したものです。
JavaScript の this の解説は巷にあふれていますが、私も this をまとめてみたかったのでここにメモ。
一般的に語られる4パターン
- メソッド呼び出しパターン
- 関数呼び出しパターン
- コンストラクタ呼び出しパターン
- call/apply/bind 呼び出しパターン
その他、覚えておかなければいけないこと
- トップレベルの
this
- 非Strictモード時の自動ボクシング
- コールバック関数における
this
- カンマ演算子
- arrow function 呼び出しパターン
- ES class は Strictモード
- Generator の場合
1. メソッド呼び出しパターン
JavaScript のメソッドはすべて関数です。オブジェクトのプロパティである関数をメソッドと呼びます。
メソッドを呼び出すときのレシーバが this値 になる。ドットの左側。
'use strict';
const obj = {
prop: 42,
func() {
return this.prop;
},
};
console.log(obj.func()); //=> 42
2. 関数呼び出しパターン
レシーバはないので、this値は undefined
になります。
'use strict';
function func() {
return this;
}
console.log(func()); //=> undefined
関数として定義したか、メソッドとして定義したかということは関係ない。
3. コンストラクタ呼び出しパターン
コンストラクタも関数であり、new
するとインスタンスを作成して実行します。
this値はそのインスタンスです。
'use strict';
function Foo() {
this.x = 0;
}
const foo = new Foo();
console.log(foo.x); //=> 0
console.log(foo.hasOwnPropaty('x')); //=> true
4. call/apply/bind 呼び出しパターン
call/apply/bind で、this値を指定できます。(まさに this値(thisArg)って言葉がよく似合う。)
(1.)メソッド呼び出しパターンと(2.)関数呼び出しパターンのどちらでも指定した値になるし、undefined
や null
を指定することもできます。
トップレベルの this
上記の4パターンはすべて、関数の this値についてでしたが、次は トップレベルの this
。
実行コンテキストが "Script" である場合、トップレベルの this
はグローバルオブジェクトとなります。
<script>
console.log(this); // => global object
(()=>{
console.log(this); // => global object
})();
</script>
実行コンテキストが "Module" である場合、トップレベルの this
は undefined
となります。必ず Strictモードになることに注意。
<script type="module">
console.log(this); // => undefined
(()=>{
console.log(this); // => undefined
})();
</script>
非Strictモード時の自動ボクシング
非Strictモードでは、this値がプリミティブならオブジェクトでラップされる。これを「自動ボクシング (auto-boxing)」というそうです。
そして、this値が undefined
または null
ならグローバルオブジェクトになります。
'use strict';
function func() {
return this;
}
console.log(func.call('foo')); //=> 'foo'
console.log(func.call(42) === 42); //=> true
console.log(func()); //=> undefined
(↑Strictモード ではボクシングされない)
with ({}) {} // not strict mode;
function func() {
return this;
}
console.log(func.call('foo')); //=> String object
console.log(func.call(42) === 42); //=> false
console.log(func()); //=> global object
(↑非Strictモード ではボクシングされる)
これまで紹介したどのパターンであっても「非Strictモードでは this値が undefined
または null
ならグローバルオブジェクトになる」ことに注意。関数呼び出しパターンはグローバルオブジェクトを返すという説明は半分正解半分間違いです。
コールバック関数における this
コールバック関数をどのように実行するかはその高階関数次第。
ネイティブ関数では this値が undefined
であることが多いが、 Array#map や Array#filter などでは this値を指定する thisArg 引数が存在します。
カンマ演算子
カンマ演算子を使えば変数に入れずに(2.)関数呼び出しパターンができます。
'use strict';
const obj = {
func() {
return this;
},
};
(obj.func)() === obj;
(0, obj.func)() === undefined;
arrow function 呼び出しパターン
arrow function では this値が引き継がれます。call/apply/bind の指定は無視されます。
ES class は Strictモード
class 宣言や class 式 では Strictモードで実行されます。つまり、自動ボクシングは起こりません。
Generator の場合
Generator関数では Generatorオブジェクトを next()
しなければ進みませんが、this値はこれまで紹介した各パターンの通り。
ただし、Generatorオブジェクトの next()
では別のオブジェクトを this値とすることはできません(例外発生)。
'use strict';
const that = { a: 42 };
const genfn = function*(){ while (true) { yield this.a; } };
const g = genfn.call(that);
console.log(g.next().value); //=> 42
//console.log(g.next.call({}).value); //~> TypeError: next method called on incompatible Object
console.log(g.next.call(g).value); // 42
その他注意点
- globalオブジェクトを取得する - Qiita
indirect eval call は常にグローバルスコープで実行されます。
Function コンストラクタは Strictモードが伝播しません。
参考文献
- JavaScriptの「this」は「4種類」?? - Qiita
- Strict モード - JavaScript | MDN
- ECMAScript 2015以降のJavaScriptの
this
を理解する | Web Scratch