# 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 | | == != === !== | 等於、不等於、嚴格相等、非嚴格相等 | | & | 按位與 | | ^ | 按位異或 | | &#124; | 按位或 | | && | 邏輯與 | | &#124;&#124; | 邏輯或 | | ?: | 條件 | | = 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/) :::