LoginSignup
0
0

JavaScript パーフェクト this

Last updated at Posted at 2024-04-27

この記事は、2018年04月05日に限定公開として作成したあと、2024年に全体に公開したものです。

JavaScript の this の解説は巷にあふれていますが、私も this をまとめてみたかったのでここにメモ。

一般的に語られる4パターン

  1. メソッド呼び出しパターン
  2. 関数呼び出しパターン
  3. コンストラクタ呼び出しパターン
  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

関数として定義したか、メソッドとして定義したかということは関係ない。

非Strictモード時の注意はここでは述べずに後述します

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.)関数呼び出しパターンのどちらでも指定した値になるし、undefinednull を指定することもできます。

トップレベルの this

上記の4パターンはすべて、関数の this値についてでしたが、次は トップレベルの this

実行コンテキストが "Script" である場合、トップレベルの this はグローバルオブジェクトとなります。

<script>
console.log(this); // => global object
(()=>{
  console.log(this); // => global object
})();
</script>

実行コンテキストが "Module" である場合、トップレベルの thisundefined となります。必ず 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

その他注意点

indirect eval call は常にグローバルスコープで実行されます。

Function コンストラクタは Strictモードが伝播しません。

参考文献

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0