--- 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`。 ![](https://i.imgur.com/QBNB8PN.png =30%x) ```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()`,再做一次渲染畫面的行為。 ![](https://i.imgur.com/LjiDAxJ.png) ```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`。 ![](https://i.imgur.com/bgYjh7v.png =40%x) ```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運算子**就變成==實體==。 :::