# Day 15：this 關鍵字 (1)

> 今天驟聞武俠大師金庸查先生逝世的消息，身為一位超過二十年的金迷，實在難以表達心中的難過。在此偷渡對一代文學大師的懷念，聊表追思，紀念這個對華文世界影響至深之偉人殞落的日子：金庸武俠，永垂不朽，緬懷再三，一路好走。—— 2018.10.30

`this` 是一個特殊的關鍵字，代表著一個物件，在很多程式語言都可以看到這個設計。由於不同程式語言有各自的特性， `this` 的運作方式也不盡相同。

那在 JavaScript 裡，`this` 指什麼呢？

來看看 W3Schools 的說明：

> * In a method, this refers to the **owner object**.
> * Alone, this refers to the **global object**.
> * In a function, this refers to the **global object**.
> * In a function, in strict mode, this is **undefined**.
> * In an event, this refers to **the element that received the event**.
> * Methods like call(), and apply() can refer this to **any object**.

JavaScript 的 `this` 一下是物件本身，一下是 Global 物件，一下是 `undefined`，一下還可以是任何物件！

![](https://i.imgur.com/RqTPySJ.png)\
(Source: [網路圖片](https://pic.pimg.tw/kenmy/1380581708-2393697494_n.jpg))

`this` 是程式語言中很重要的部分，誤判 `this` 所代表的物件會直接對程式的運作造成影響。所以本篇文章就來好好弄清楚 JavaScript 的 `this`。

## `this` 的簡單範例

照例先上一個基本款的情境範例：

```javascript
var ironMan = {
    firstName: "Tony",
    lastName : "Stark",
    getFullName : function() {
        return this.firstName + " " + this.lastName;
    }
};

console.log(ironMan.getFullName()); // "Tony Stark"
```

這是一個典型的 `this` 應用例子，對物件進行物件導向風格的操作。上面這個例子看起來很好懂，那 JavaScript 的 `this` 到底複雜在哪？

## JavaScript 的 `this` 很複雜？

像 `Java`、`C#` 等基於類別的物件導向語言 (Class-based Object-oriented Languages)，由於語法規範極為嚴謹，大多數情境可以從語彙範圍 (Lexical Scope) —— 也就是看語法上定義在哪個地方來判斷。

但 JavaScript 的 `this` 關鍵字運作與其他語言不同。具體來說，**JavaScript 的 `this` 不是看定義的語彙位置，而是根據執行當下誰擁有這段程式碼**。

> W3Schools: `this` has different values depending on where it is used. The JavaScript `this` keyword refers to the object it belongs to.

換句話說，**不是看該屬性或函式被定義在哪個物件內，看的是執行當下被誰呼叫**。

JavaScript 的語彙定義上沒有像類別那樣完整明確的物件界線，加上語法結構設計上很寬鬆，衍生出各種複雜情境。

此外，不同情境下，`this` 運作的機制也不同，例如：

* 全域執行環境 (Global Context) 下和函數執行環境 (Function Context) 下不同。
* 在嚴謹模式 (Strict Mode) 與一般模式下也可能有所不同。

![](https://i.imgur.com/lcqSy7g.png)\
(Source: [網路圖片](https://colorfulblanche.com/wp-content/uploads/2018/01/%E6%8A%95%E5%BD%B1%E7%89%8713-1024x576.png))

理論描述太抽象，接下來會試圖用各種實際的例子來探討 `this` 的運作。

## 全域執行環境下 (Global Context)

全域環境下比較單純。

> MDN: In the global execution context (outside of any function), this refers to the global object whether in strict mode or not.

* **`this` 在所有函式以外的全域執行環境下，會被當作全域物件，無論是否處於嚴謹模式**。
  * HTML 中就是 `window` 物件。
  * Node.js 中就是 `global` 物件。

例如在全域執行環境下直接印出 `this` 物件：

一般模式：

```javascript
console.log(this); // window object
```

嚴謹模式：

```javascript
"use strict";

console.log(this); // window object
```

## 函數執行環境下 (Function Context)

函數環境下可能遇到的情境就複雜了。

> MDN: Inside a function, the value of this depends on how the function is called.

**在函數內的 `this` 值取決於該函數如何被呼叫。**

就先記著一個大原則：**看是誰呼叫的**，然後用這個原則來看各種情境下 `this` 運作的例子。

### 1. 物件函式 (As an Object Method)

> `this` 物件：呼叫者本身。

這應該是最常見、也相對好理解的情境，也就是 Object Method Binding。

在物件函式的用法，嚴謹模式或一般模式都是一樣的執行結果。

但同樣是物件函式的用法，也有很多種語法情境。

#### 1.1. 函數被定義在物件之內

下面例子中 `player` 物件有 2 個函式，其中一個直接在函數內回傳 `this`，藉此來觀察當下的 `this` 值：

```javascript
var player = {
  name: 'OneJar',
  getName: function() {
    return this.name;
  },
  whatsThis: function() {
    return this;
  },
};

console.log(player.getName());      // "OneJar"
console.log(player.whatsThis());    // {name: "OneJar", getName: ƒ, whatsThis: ƒ}    # `player` object
```

* 執行的函數是 `player.whatsThis` 和 `player.getName` 所指向的函數。
* 呼叫者是 `player`。
* 因此函數內的 `this` 指的是 `player`。

這個例子理解上應該沒什麼困難，很單純的呼叫物件函式用法，加上函數定義就在物件內，判斷上很簡單。

如果函數定義不在物件內呢？

#### 1.2. 借用函數 (函數被定義在物件之外)

函數不是物件本身自己定義，而是指向別人的函數，就像跟別人借用函數一樣。

那在函數裡的 `this`，會是物件本身，還是別人？

是不是開始有趣了。

來看下面的例子：

```javascript
var getName = function() {
    return this.name;
};
var whatsThis = function() {
    return this;
};

var player = { name: 'OneJar' };
player.f1 = getName;
player.f2 = whatsThis;

console.log(player.f1());     // "OneJar"
console.log(player.f2());     // {name: "OneJar", getName: ƒ, whatsThis: ƒ}    # `player` object
```

* `getName()` 和 `whatsThis()` 函數都不是定義在 `player` 內。
* 但執行當下，`player` 是呼叫者，被視為那段程式碼的擁有者，因此 `this` 仍是 `player`。

**函數是否為物件本身的語彙構件無所謂，誰是最直接的呼叫才是最重要的**。

#### 1.3. 物件的屬性物件的函式

物件內的屬性可以是另一個物件，另一個物件也可以有自己的函式，那這時候的 `this` 是誰？

例如以下例子：

```javascript
var getName = function() {
    return this.name;
};

var player = {
  name: 'OneJar',
  f: getName,
  pet: {
    name: 'Totoro',
    f: getName,
  }
};

console.log(player.f());      // "OneJar"
console.log(player.pet.f());  // "Totoro"
```

* `player` 物件擁有另一個物件 `pet`，而 `player` 和 `pet` 都借用 `getName()`。
* `player.f()` 的呼叫者是 `player`。
* `player.pet.f()` 的呼叫者是 `player.pet`。

這裡的原則沒有變，一樣看**誰是呼叫者，誰就是 `this`**。

只是不要混淆，`player.pet.f()` 的呼叫者是 `player.pet` 而不是 `player`。

## References

* [W3Schools - The JavaScript this Keyword](https://www.w3schools.com/js/js_this.asp)
* [this - JavaScript | MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this)
* [#Javascript：this用法整理 | 英特尔® 软件](https://software.intel.com/zh-cn/blogs/2013/10/09/javascript-this)
* [JavaScript 語言核心（11）this 是什麼？ by caterpillar | CodeData](https://www.codedata.com.tw/javascript/essential-javascript-11-what-is-this/)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://something-about-js-book.onejar99.com/day15.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
