---
title: JavaScript核心篇 - this
tags:
description:
---
JavaScript核心篇 - this
===
this
---
### 1. this跟函式作用域有關係
- 一個函式作用域可以取得四個項目`myName`、`arguments`、`this`、`window`。
- `myName`是傳入的參數,`argument`是所有傳入的參數。
- `this`是當前的作用域(本身可以取到),`window`是往外層找(全域)。
```javascript=
function fn(myName) {
console.log(myName, arguments, this, window);
};
fn('小明', 1, 2);
```
<br>
### 2. 全域環境下的this
- 在全域設定屬性myName,全域的小明。
- **全域環境**下console.log()可以直接印出myName的值。
```javascript=
window.myName = '全域的小明';
console.log(myName);
```
- **this**本身就==存在全域的作用域之下==。
- 全域的環境下this==直接指向window==,this就等同於window。
```javascript=
console.log(this);
console.log(this === window); // true
```
- `this.myName === myName`為`true`,可證明**this就等同於window**。
```javascript=
console.log(this.myName); // 全域的小明
console.log(myName); // 全域的小明
console.log(this.myName === myName); // true
```
:::success
實戰開發不建議使用**全域的this**。
:::
<br>
### 3. 純粹的呼叫
- 純粹的呼叫是指直接call `fn()`,沒有經過**任何處理**或**物件`obj.fn()`**。
- 純粹的呼叫的this**也是直接指向全域**。
```javascript=
window.myName = '全域的小明';
function fn() {
console.log(this);
console.log(this.myName);
};
fn();
```
:::success
**直接指向全域的this**,實戰開發不建議使用。
:::
:::danger
`直接呼叫`、`callback function`、`立即函式`,**this**都是指向全域(window)。
:::
```javascript=
window.myName = '全域的小明';
// 立即函式
(() => {
console.log(this);
})();
// callback function
[1, 2, 3].forEach(item => console.log(this));
```
<br>
### 4. 物件下的方法(最常見,佔80%左右)
:::success
this的指向在於**如何呼叫它**。
:::
- **this**被限制在`callName()`的範圍裡面。
- `callName()`前面有個物件`family`,將this作為物件傳進`callName()`。
```javascript=
const family = {
myName: '小明家',
callName() {
console.log(this.myName);
},
};
family.callName(); // 小明家
```
- **this**的指向`ming`裡面的`callName()`,`this.myName`為小明。
```javascript=
const family = {
myName: '小明家',
callName() {
console.log(this.myName);
},
ming: {
myName: '小明',
callName() {
console.log(this.myName);
},
}
};
family.ming.callName();
```
- callName()前面是**物件family**,將this作為**物件**,經由family傳進**物件屬性**callName。
```javascript=
function callName() {
console.log(this.myName);
};
const family = {
myName: '小明家',
callName: callName,
ming: {
myName: '小明',
callName() {
console.log(this.myName);
},
}
};
family.callName(); // 小明家
```
---
<br>
### this的作用
:::success
this**概念**上,是一個==代名詞==。
:::
this上的`callName()`,`this`指的就是`family`。
```javascript=
family.callName()
```
<br>
#### 1. 現在要模擬一個行為,來==解釋this的作用==。
1. 元件初始化
2. 取得資料並更新文字
3. 進行渲染
- `component.init()`去執行物件`component`內的函式來做初始化。
- 在`component`內`init()`執行初始化,也要從`getData()`取得資料,該如何執行?
- 就是`this.getData()`,==this指的就是`component`本身==。
- 但`getData()`裡面的`setTimeOut()`(模擬遠端取得資料)是`callback function `所以`this`指向全域`window`。
```javascript=
let component = {
text: '這是預設文字',
getData() {
console.log(this);
console.log('取得資料');
setTimeout(function (){
console.log(this);
}, 1000);
},
init() {
console.log(this);
this.getData();
},
};
component.init(); // 取得資料
```
#### 2. 但是setTimeOut()內的this指向到全域window,有什麼辦法能==改成指向`component`本身==?
- 可以用`that`取代`this`,`const that = this`**傳參考**的概念。
- `that.text = '被替換的文字'`把原本文字給替換掉。
```javascript=
let component = {
text: '這是預設的文字',
getData() {
console.log(this);
const that = this;
console.log(that);
setTimeout(function () {
that.text = '被替換的文字'
}, 1000);
},
init() {
console.log(this);
this.getData();
},
};
component.init();
```
#### 3. 接著該如何渲染到畫面上?
- `setTimeOut()`內加上`that.render()`。
- `this.el.innerHTML = this.text`,把指向`conponent`的屬性`text`**賦予給**指向`conponent`的屬性`el`。

```htmlmixed=
<div class="root"></div>
```
```javascript=
let component = {
text: '這是預設的文字',
el: document.querySelector('.root'),
getData() {
console.log(this);
const that = this;
console.log(that);
setTimeout(function () {
that.text = '被替換的文字';
that.render();
}, 1000);
},
init() {
console.log(this);
this.getData();
},
render() {
console.log('我要渲染到畫面上');
this.el.innerHTML = this.text;
},
};
component.init();
```
#### 4. 如何重複運用
把`component`用**淺層拷貝**的方式賦予給變數`component2`。
- 淺拷貝給`component2`之後,更改屬性`el`的DOM元素。
- 執行`component2.init()`,再做一次渲染畫面的行為。

```htmlmixed=2
<div class="root2"></div>
```
```javascript=23
component.init();
let component2 = {
...component,
el: document.querySelector('.root2'),
};
component2.init();
```
:::success
這就是用代名詞(**this**)的概念,讓程式碼**重複運用**,只需要修改部分。
:::
---
<br>
### 箭頭函式的this
:::success
因為箭頭函式**沒有自己的this**,所以要看外一層的this**指向**。
往外一層找不到,再往外一層找,**直到找到傳統函式為止**,如果都**找不到就指向全域**。
:::
```javascript=
window.myName = '全域';
(function () {
const myName = '區域';
(() => {
console.log(this); // 全域
})();
})();
```
<br>
**範例:**
- ==箭頭函式沒有自己的this==,`callName()`的外層沒有其他作用域,所以`family.callName()`的`this`指向**全域window**。
```javascript=
window.myName = '全域';
const family = {
myName: '小明家',
callName: () => {
console.log(this.myName);
},
ming: {
myName: '小明',
callName: () => {
console.log(this.myName);
},
},
};
family.callName();
```
<br>
**範例:**
- 把`callName`改成**傳統函式**,**this**就會指向`family`。
- 所以`this.myName`是'小明家'
```javascript=5
callName() {
console.log(this.myName); // 小明家
},
```
<br>
**範例:**
- `setTimeout()`改用箭頭函式**沒有自己的this**,所以**看外層**。
:point_right: 以往是用`that`或`vm`,改變`this`指向,**現在**都==直接用箭頭函式==。
- this是代名詞的概念,所以`this.myName`指向`family`。
```javascript=
window.myName = '全域';
const family = {
myName: '小明家',
callName() {
setTimeout(() => {
console.log(this.myName);
}, 1000);
},
ming: {
myName: '小明',
callName: () => {
console.log(this.myName);
},
},
};
family.callName();
```
**範例:**
- 看**箭頭函式的外一層**`callName`的`this`指向。
- 以代名詞概念`this.callName()`指向`ming`,`this.myName`是'小明'。
```javascript=
window.myName = '全域';
const family = {
myName: '小明家',
callName() {
setTimeout(() => {
console.log(this.myName);
}, 1000);
},
ming: {
myName: '小明',
callName() {
(() => {
console.log(this.myName);
})();
},
},
};
family.ming.callName();
```
---
<br>
#### new 運算子的this指向
:::success
new 運算子可以**快速**且**重複建立相同結構**的物件。
:::
- function的名稱要**用大寫開頭**。
- New運算子**後面**會加上函式,賦予給變數,**將變數作為物件**代入`this`。
:point_right: New會建立新的物件。
- 也就是說`this.name`,等同`joe.name`。

```javascript=
function Person() {
this.name = '小明';
};
const joe = new Person();
console.log(joe);
```
**可以寫成**
```javascript=
function Person(name) {
this.name = name;
};
const joe = new Person('小明');
console.log(joe);
```
:::success
**Person()**可以想像是一張**藍圖**,經過**New運算子**就變成==實體==。
:::