# ES6 學習筆記
###### tags: `ReactJS` `ES6` `Javascript`
本篇筆記是基於 Udemy 線上課程 [快速學習 React.js 和 Redux 的基礎到實踐](https://www.udemy.com/reactjs-redux/) 再加上自己想深入了解的部分請教 Google 大神後整理出來的筆記。
## 常數及變數 (Constants and Variables)
| 語法 | 作用域 | 類型 |
| --- | --- | --- |
| var | 函式作用域 | 沒有常數的概念,只有分全域變數或區域變數 |
| let | 區塊作用域 | 變數 |
| const | 區塊作用域 | 常數 |
* 函式作用域 (function scope):`function` 內的作用區域。
* 區塊作用域 (block scope):區塊語句內的作用區域,如:`function` `if` `else` `for` `while`..等。
* 全域變數:於 function 外宣告的變數,在整個程式中都可被存取與修改。
* 區域變數:於 function 內宣吿的變數,`function` 外的區域皆無法存取這個變數,且每次執行 function 時都會重新再宣告一次。
* 常數 (constants):一個固定的值,不能被改變。
* 變數 (variables):一個可修改的容器,能在程式中不斷被修改。
## 樣板字串 (Template literals)
使用重音符 ` 表示
```
const helloText = `hello`
```
可以有多行文字
```
const helloText = `hello
world`
```
可以使用變數 / 常數、運算式、函式,需用錢字號及大括號包住變數,如:${yourVar}
```js=
//取得目前年份
const year = new Date().getFullYear();
const myAge = `我今年${year - 1990}歲`;
console.log(myAge);
//執行結果則為:我今年29歲
```
## 短路求值/最小化求值 (Short-circuit evaluation)
使用邏輯運算符號 (`&&` `||` `!`) 求值的策略,當第一個運算數的值無法確定結果時,才對第二個運算數進行求值。
| 布林值 | 結果 |
| --- | --- |
| false | 0, -0, null, NaN, undefined, 空白字串(""), false |
| true | 不為上述結果皆為真,>=1, <=-1, 非空白字串:(" ")也算, true |
短路求值範例:
```js=
const obj = obj || {};
//等同於
if(!obj){
obj = {};
}
```
```js=
var a = 5, b;
if (a == 3) {
b = 1;
} else if (a == 5) {
b = 2;
} else {
b = 3 ;
}
console.log(b); //執行結果為:2
//等同於
var a = 5,b;
b = a == 3 && 1 || a == 5 && 2 || 3;
```
### 運算子的優先順序
| 運算符 | 描述 |
| --- | --- |
| . [] () | 欄位訪問、陣列下標、函式呼叫以及表示式分組 |
| ++ -- - ~ ! delete new typeof void | 一元運算子、返回資料型別、物件建立、未定義值 |
| * / % | 乘法、除法、取模 |
| + - + | 加法、減法、字串連線 |
| << >> >>> | 移位 |
| < <= > >= instanceof | 小於、小於等於、大於、大於等於、instanceof |
| == != === !== | 等於、不等於、嚴格相等、非嚴格相等 |
| & | 按位與 |
| ^ | 按位異或 |
| | | 按位或 |
| && | 邏輯與 |
| || | 邏輯或 |
| ?: | 條件 |
| = oP= | 賦值、運算賦值 |
| , | 多重求值 |
## 展開運算子 (Spread Operator) 、 其餘參數 (Rest Operator)
展開運算子及其餘參數表現方式皆為 `...value`。
### 其餘參數
其餘參數可讓我們表示不確定數量的參數,並將其**轉為一個陣列**。
:::warning
* 一個函式只能有一個其餘參數
* 必須放在所有參數的最後面
:::
```js=
function sum(...numbers) {
var result = 0;
numbers.forEach(function (number) {
result += number;
});
return result;
}
console.log(sum(1, 2, 3, 4, 5));
//執行結果為:15
```
### 展開運算子
展開運算子是把陣列拆解成一個一個的值。原理如下:
```js=
const values = [1,2,3];
const copyValues = [...values];
//等同於
const values = [1,2,3];
let copyValues = [];
for(let i=0; i<values.length; i++){
copyValues.push(values[i]);
}
```
傳入陣列
```js=
let number_arr = [1,2,3,4,5];
console.log(...number_arr);
//執行結果為:1, 2, 3, 4, 5
```
傳入字串
```js=
let str = 'Hello'
console.log(...str);
//執行結果為:"H", "e", "l", "l", "o"
```
也可把陣列展開來,傳入函式中:
```js=
function sum(a, b, c) {
return a + b + c;
}
var args = [1, 2, 3];
console.log(sum(...args));
// 執行結果為6
```
### 類陣列轉成純陣列
類陣列物件:寫法和陣列一樣,如 arguments、dom 陣列,類陣列**擁有 length 屬性**也跟陣列一樣**可以用索引訪問元素**,但無法使用陣列的方法 (forEach、map、reduce)。
因此我們可以透過展開運算子將類陣列轉成純陣列
```js=
function sum() {
let arg = [...arguments];
//透過展開運算子來轉成純陣列
let total = 0;
arg.forEach(function(element) {
total += element;
});
return total;
}
console.log(sum(1, 2, 3, 4, 5));
// 執行結果為15
```
## 類別 (Class)
### 建構式
```javascript=
class product{ //類別
constructor(color,price){ //建構式
this.color = color;
this.price = price
}
}
```
* 於類別中宣告的函式
* 只會於建立時呼叫一次
* 可省略不寫
* 一個類別中只能有一個建構式
## Object.assign 物件複製
```
Object.assign(target, ...sources)
```
* 可合併物件
* 若 key 值相同則後者覆蓋前者
* 複製方式為淺層複製,不會另外複製子物件,只會記憶原物件的子物件
合併物件範例:
```javascript=
const fruit1 = { one: 'apple'};
const fruit2 = { two: 'orange'};
const fruit2 = { three: 'banana'};
const fruits = Object.assign({}, fruit1,fruit2,fruit3);
console.log(fruits);
//執行結果為 {one: 'apple', two: 'orange', three: 'banana'}
```
key 值相同範例:
```javascript=
const amy = { hair: 'short', weight: '52', height: '172'};
const jessica = { weight: '48', height: '158'};
const obj = Object.assign(amy, jessica);
console.log(obj);
//執行結果為 { hair: 'short', weight: '48', height: '158'}
```
淺層複製範例:
```javascript=
const amy = {
hair: {length:'short',color:'black'},
weight: '52',
height: '172'
};
const nana = Object.assign({}, amy);
amy.hair.length = 'long';
console.log(nana.hair.length);
//執行結果為 long
```
### 深層複製方式
將原物件轉會成 JSON 格式,再解析 JSON 格式變能成為新的物件。
:::warning
注意:若物件內含有函式,則函式會消失,因為 JSON 只能存放資料,不能存放函式
:::
```javascript=
const amy = {
hair: {length:'short',color:'black'},
weight: '52',
height: '172',
hello: function(){console.log('hello!')}
};
const nana = JSON.parse(JSON.stringify(amy));
amy.hair.length = 'long';
console.log(nana.hair.length);
//執行結果為 short
console.log(nana.hello());
//執行結果會出現錯誤訊息:
//TypeError: nana.hello is not a function
```
## 模組系統
* 以檔案為單位,一個檔案即一個模組
* 透過 export 及 import 語法輸出與輸入
### export
* 可輸出任何資料:函式、類別、字串、布林、數字...
* 單一模組可輸出多筆資料
```javascript=
// product-module.js
export const sayHello = 'Hello!';
export const banana = {
color: 'yellow',
amount: 293,
place: 'Taiwan'
};
export class fruit {
constructor(name, color, place){
this.name = name;
this.color = color;
this.place = place;
}
sayHello = function(){
console.log(this.name + 'say hello!')
}
}
```
### import
```
import { 欲輸入的export項目 } form '模組路徑';
```
以上面模組示範:
```javascript=
import {sayHello, fruit} form './product-module';
console.log(sayHello); //執行結果為 Hello!
const banana = new fruit('banana', 'yellow', 'Taiwan');
banana.sayHello(); //執行結果為 banana say hello!
```
通常模組只會有一個輸出,可使用 `default` 語法指定預設輸出,如此也可省略 import 時的大括號及更換模組名稱避免命名衝突。
```javascript=
// product-module.js
// 於 export 後加上 default 語法
export default class fruit {
construtor(name, color, place){
this.name = name;
this.color = color;
this.place = place;
}
sayHello = function(){
console.log(this.name + 'say hello!')
}
}
```
import 則可改成:
```javascript=
import DefineFruit form './product-module';
```
也可使用 * 匯入所有輸出:
```javascript=
import * as DefineFruit form './product-module';
```
## 解構賦值
```
const { key: newName } = user;
```
範例:
```javascript=
const amy = {
hair: 'short',
weight: '52',
height: '172'
};
const {hair:newHair} = amy;
console.log(newHair);
//執行結果為 short
```
若新名字與舊名字相同則可簡寫為:
```javascript=
const {hair} = amy;
```
### 陣列解構
若不需前面的值,依然需要保留逗號。
```javascript=
const arr = [1,2,3,4,5];
const [,,three,...restNum] = arr;
console.log(three); //3
console.log(restNum); //[4,5]
```
## 預設值
ES6 可直接於 function 的括號內給予參數預設值。
```javascript=
class fruitData{
constructor(name, color, place='unknown'){
this.name = name;
this.color = color;
this.place = place;
}
}
const fruit = new fruitData('Banana','red');
console.log(fruit.place); //unknown
```
## 箭頭函式(Arrow function)
```javascript=
//ES5寫法
function sayHi(){ console.log('Hi!'); }
sayHi();
//ES6寫法
const sayHi = () => { console.log('Hi!'); }
sayHi();
```
傳入單一參數時無須括號
```javascript=
const sayHi = name => { console.log(name + 'Hi!'); }
sayHi(Amy);
```
傳入單一參數但有設定預設值時則不可省略括號
```javascript=
const sayHi = (name = 'Jessica') => { console.log(name + 'Hi!'); }
sayHi();
```
無參數時括號不可省略
```javascript=
const sayHi = () => { console.log('Hi!'); }
sayHi();
```
若函式只有一行且直接 return 則可省略大括號
```javascript=
const getName= user => user.name;
const name = getName({
name: 'Amy',
mail: 'amy@mail.com'
});
console.log(name);//Amy
```
:::warning
箭頭函式內的`this` 將指向於宣告箭頭函式當下的環境,不會隨著使用的時機而改變
:::
## 異步運算(Promise)
語法如下:
```
const promise = new Promise((resolve,reject)=>{
if(true){
resolve('good');
}
reject('not good!');
});
```
```
promise.then((result)=>{
console.log('resolve result='+result);
}, (reason) =>{
console.log('reject reason='+reason);
});
```
### Promise 三態
| | 狀態 | 結果 |
| --------- | ------- | ---------- |
| 未解 | pending | |
| 已解,完成 | settled | fullfilled |
| 已解,失敗 | settled | rejected |
### 錯誤處理
```
promise.catch((error)=>{
console.log(error);
});
```
等同於
```
promise.then(undefined,(error)=>{
console.log(error);
});
```
### 參考來源
:::info
* [ES6-展開運算子(Spread Operator)、其餘參數(Rest Operator)](https://kanboo.github.io/2018/01/26/ES6-SpreadOperator-RestOperator/)
* [JavaScript 類陣列物件與 arguments](https://www.itread01.com/yyicx.html)
* [關於 JS 中的淺拷貝和深拷貝](http://larry850806.github.io/2016/09/20/shallow-vs-deep-copy/)
:::