---
# System prepended metadata

title: '克服 JS 奇怪的部分 22.  物件、函數與「this」 '
tags: ["JavaScript\_", JavaScript 基礎]

---

### 函數 & 執行環境：

- 當函數被呼叫時，一個新的**執行環境**會被建立起來：
  - **這部分先不要跟物件搞混**。
```
// 詞彙
Execution 執行
Context 上下文
```

- 每個執行環境(Execution context)，都有自己的**變數環境**：
  - 亦可參考到**外部環境**(outer environment)。
```
// 詞彙
variable environment 變數環境
outer environment 外部環境
```

### this

- 當我們產生執行環境時，JavaScript Engine 會幫我們產生我們從未宣告過的一個變數 - `this`。

- 在某些情況下，this 會根據環境決定它應該指向誰。
  - 在以下範例，你會發現輸出的結果是 window
```js
function a() {
  console.log(this);
}

a();

// outputs:
window
```
- 這代表什麼？這代表我們宣告了一個函數以後，在函數執行 `this`
  它**仍然會指向全域物件**。
  
### 多宣告一個變數 b

```js
function a() {
  console.log(this);
}

var b = function () {
  console.log(this);
};

a();
b();

// 兩個輸出的結果都是一樣 -> window
```

- 嘗試建立物件：

```js
function a() {
  console.log(this);
  this.newvariable = `hello`;
}

var b = function () {
  console.log(this);
};

a(); // window

console.log(newvariable); // hello

b(); // window
```
- 當我們呼叫函數時，this 指向全域物件，要能理解它，否則在開發上會發生很多奇怪的錯誤。

### 在物件中呼叫函數，this 會得到什麼？

- 指向到物件本身。

```js
var c = {
  name: `The c object`,
  log: function () {
    console.log(this);
  },
};

c.log();

// outputs:
{name: 'The c object', log: ƒ}
```

```
函數 a -> this 指向全域
函數 b -> this 指向全域
物件 c -> this 指向物件本身
```

- 在物件中的函數，產生 this 屬性會發生什麼事？
  - 這時候 this 指向到物件內的函數中的屬性 name
```js
var c = {
  name: `The c object`,
  log: function () {
    this.name = `update c object`;
    console.log("this:", this);
  },
};

c.log();

// outputs:
this: {name: 'update c object', log: ƒ}
```

### c 物件與 this 預期外的錯誤？


```js
var c = {
  name: `The c object`,
  // 透過 c.log() 呼叫，這邊的 this 是指向物件 c
  log: function () {
    this.name = `update c object`;
    console.log(this);
    
    // 表達式宣告函數 -> this 指向 window（全域）底下的 c 物件
    var setname = function (newname) {
      this.name = newname;
    };
    
    setname("Updated again! The c object");
    
    // 第二次輸出，this 仍然指向物件 c
    console.log(this);
  },
};

c.log();
// outputs:
{name: 'update c object', log: ƒ} // 這邊 this 指向物件
{name: 'update c object', log: ƒ} // 這邊 this 指向全域
```

- 這裡使人感到困惑的點，是為何下一個 log 也 print 一個相同的結果。


### 解決方式-1：

- 將 this 賦值給一個變數：


```js
var c = {
  name: `c is a object variable`,
  log: function () {
    var self = this;
    self.name = `update c object!`;
    console.log(self);

    var setname = function (newname) {
      self.name = newname;
    };
    setname("update again c object");
    console.log(self);
  },
};

c.log();

```


### 解決方式-2：

- **使用箭頭函數**，因為箭頭函數不會創建自己的 this，**而是繼承自外部作用域的 this**。

```js
const c = {
  name: `The c is a object`,
  log: function () {
    this.name = `update object c!`;
    console.log(this);

    // 這邊改用 arrow function:
    const setname = (newname) => {
      this.name = newname;
    };
    setname("updated again object c");
    console.log(this);
  },
};

c.log();

```
### 後續：

- 本篇幅概念比較複雜一些，需要更深入的理解 this。



