javascript の「this」について

## グローバルコンテキスト

node.js

グローバル空間での this は`{}`

```javascript
console.log(this); // {}
this.a = 'MDN';
console.log(this); // { a: 'MDN' }
```

Browser

グローバル空間での this は`windowオブジェクト`

```javascript
console.log(this); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
this.a = 'MDN';
console.log(this); // Window {a: "MDN", postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
```

## 関数コンテキスト

### 単純な呼び出し

node.js

this には`global`オブジェクトが入る

```javascript
function f1() {
  return this;
}
console.log(f1()); // console: [Getter],DTRACE_NET_SERVER_CONNECTION: [Function],DTRACE_NET_STREAM_END: [Function],…
console.log(f1() === global); // true
```

node.js + strict mode

strict mode だと this は未定義となる。

```javascript
'use strict';
function f1() {
  return this;
}
console.log(f1()); // undefined
console.log(f1() === global); // false
```

Browser

グローバルコンテキスト同様、this には window オブジェクトが入る

```javascript
function f1() {
  return this;
}
console.log(f1()); // Window {postMessage: ƒ, blur: ƒ, focus: ƒ, close: ƒ, frames: Window, …}
console.log(f1() === window); // true
```

Browser + strict mode

node.js 同様、stirct mode だと this は未定義となる

```javascript
'use strict';
function f1() {
  return this;
}
console.log(f1()); // undefined
console.log(f1() === window); // false
```

### 別のコンテキストからの渡された this

`call/apply`で function 内の this を変更できる。

node.js

`this.a`は未定義になる。

```javascript
var obj = { a: 'Custom' };
var a = 'Global';

function whatsThis(arg) {
  return this.a;
}

console.log(whatsThis()); // undefined
console.log(whatsThis.call(obj)); // 'Custom'
console.log(whatsThis.apply(obj)); // 'Custom'
```

Browser

`this.a`に`var a`で宣言された値が入る。

```javascript
var obj = { a: 'Custom' };
var a = 'Global';

function whatsThis(arg) {
  return this.a;
}

console.log(whatsThis()); // 'Global'
console.log(whatsThis.call(obj)); // 'Custom'
console.log(whatsThis.apply(obj)); // 'Custom'
```

Browser/node.js + strict mode

`this`が定義されていないため、プロパティに代入しようとした時点で TypeError が出力される。

```javascript
'use strict';
var obj = { a: 'Custom' };
var a = 'Global';

function whatsThis(arg) {
  return this.a; // TypeError: Cannot read property 'a' of undefined
}

console.log(whatsThis());
console.log(whatsThis.call(obj));
console.log(whatsThis.apply(obj));
```

### bind メソッドの this

Browser/node.js

bind を使用するとオリジナルの関数(`f()`)と同じスコープを持つ新しい関数を生成する。`this`は引数に渡されたオブジェクトに束縛される。
また、bind で生成された関数(`g()`)から bind を呼び出しても、this の参照先は変わらない。

```javascript
function f() {
  return this.a;
}

const g = f.bind({ a: 'azerty' });
console.log(g()); // azerty

let h = g.bind({ a: 'yoo' });
console.log(h()); // azerty

h = f.bind({ a: 'yoo' });
console.log(h()); // yoo

const o = {
  a: 37,
  f: f,
  g: g,
  h: h
};
console.log(o.f(), o.g(), o.h()); // 37, azerty, azerty
```

### アロー関数の this

アロー関数はそれを囲むオブジェクトを this として参照する。

以下の例ではアロー関数 x は bar メソッドを持つオブジェクト、つまり変数 `obj` を返す。

Browser/node.js

```javascript
const obj = {
  bar: function() {
    const x = () => this;
    return x;
  }
};

const fn = obj.bar();
console.log(fn()); // { bar: [Function: bar] }
console.log(fn() === obj); // true
```

\* strict mode であっても違いはない。

function だけでの呼び出しだと、global/window オブジェクトを返す。

```javascript
function bar() {
  const x = () => this;
  return x;
}

const fn = bar();
console.log(fn()); // global/windowオブジェクト
```

strict mode だと未定義になる。

```javascript
'use strict';
function bar() {
  const x = () => this;
  return x;
}

const fn = bar();
console.log(fn()); //undefined
```

補足 1

bar メソッ  ドをアロー関数にするとどうなるか?

アロー関数自身は`thisを束縛しない`ため、this は空のオブジェクトとなる。

```javascript
const obj = {
  bar: () => {
    const x = () => this;
    return x;
  }
};

const fn = obj.bar();
console.log(fn()); // {}
console.log(fn() === obj); // false
```

補足 2

一度設定された`this`は生成されたときの値が設定される。
object のメソッドとしても、call/bind で呼び出しても this は{}のまま。

```javascript
var foo = () => this;
console.log(foo()); // {}
var obj = { foo: foo };
console.log(obj.foo()); // {}
console.log(foo.call(obj)); // {}
foo = foo.bind(obj);
console.log(foo()); // {}
```

### オブジェクトのメソッドとしての this

関数がオブジェクトのメソッドとして呼び出されるとき、その this にはメソッドが呼び出されたオブジェクトが設定される。

Browser/node.js

```javascript
const o = {
  prop: 37,
  f: function() {
    return this.prop;
  }
};

console.log(o.f()); // 37
```

\* strict mode であっても違いはない。

これはどんな定義の仕方でも変わらない。オブジェクトのメンバとして呼び出されることが重要。

```javascript
const o = { prop: 37 };

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // 37
```

当然だが、呼び出し元が変わると this も変わる。

```javascript
const o = { prop: 37 };

function independent() {
  return this.prop;
}

o.f = independent;

console.log(o.f()); // 37
o.b = { g: independent, prop: 42 };
console.log(o.b.g()); // 42
```

\* strict mode であっても違いはない。

オブジェクトのプロトタイプチェーン上の this の例もあったが...プロトタイプチェーンをいまいち理解していないため割愛...。

### コンストラクタの this

関数をコンストラクタとして使用する時、this は生成された新しいオブジェクトを参照する。

`C2`は return 句でオブジェクトを返すため、`this.a`は参照されていない。

Browser/node.js

```javascript
function C() {
  this.a = 37;
}

var o = new C();
console.log(o.a); // 37

function C2() {
  this.a = 37;
  return { a: 38 };
}

o = new C2();
console.log(o.a); // 38
```

\* strict mode であっても違いはない。