###### tags: `Javascript` `ES6` `模組化` `Yarn` `Jest` `NPM`
# JavaScript 102 ─ 重點整理
## 1. 模組化
將大功能切分成小模組,避免互相影響及方便 Debug。模組有可能為 node.js 內建,或者是自己寫的。

### 如何引用模組(module)
語法:`require()`
```Javascript=
let abc = require('os');
```
os = Operating System(作業系統)
### 查詢作業系統
語法:`.platform()`
```Javascript=
let abc = require('os');
console.log(abc.platform());
```
### 如何出借模組
首先須先新增一 Js 檔案,將此檔案作為模組的輸出端,輸出你想要輸出的涵式(此為輸出 double 這個涵式)
#### 1. 單純輸出
輸出端語法:`module.exports` / `exports.物件鑰匙`
```Javascript=
function double(n) {
return n * 2
}
module.exports = double;
```
```Javascript=
function double(n) {
return n * 2
}
exports.double = double;
```
輸入端語法:`require(./檔案名稱)`
```Javascript=
let abc = require('./newone');
console.log(abc(3));
#expected answer:6 #因為輸入double這個涵式
```
mudule.exports 後面等於不僅可以放涵式,也可以放陣列、字串、數值、物件等。放涵式就是輸出涵式、放陣列就是輸出陣列。
#### 2. 將眾多功能結合在物件中
輸出端
```Javascript=
function double(n) {
return n * 2
}
module.exports = {
double: double,
triple: function(n) {
return n * 3
},
minus: function(n) {
return n - 2
}
}
```
輸入端:
```Javascript=
let abc = require('./newone');
console.log(abc.double(3), abc.triple(4), abc.minus(5));
//expected answer = 6 12 3
```
## 2. NPM(Node Package Manager)
透過此管理別人寫好的套件 ( Package )。
### 如何使用
[NPM官網](https://www.npmjs.com/),從官網內找到欲使用的功能,將下載指令輸入 cmder,欲使用此功能的檔案務必與下載下來的套件(都放在 node_module 資料夾內)在同一層。這樣才可以順利使用。
#### 下載 left-pad 套件
`npm install left-pad --save`
#### `--save`的用意
為了要將此套件寫入 package.json 內。
#### 甚麼是 package.json
此為描述此專案的檔案。裡面會詳細記錄子專案使用的套件、專案建立者、名稱等等。
#### 如何建立 package.json:`npm init`
輸入後看需求輸入使用者名稱、版本...等等。接著使用`ls`確認資料夾內檔案,即會發現一個檔案名稱為 package.json。
如果都將下載的套件都寫入 package.json 的話,就可以將 node_module 資料夾刪掉,再 commit 到 GitHub 上。或是將 node_module 放入 ignore 資料夾內。
#### .json 檔內 scripts 使用說明

scripts 內可輸入任意快速指令。如圖第一個 key 為 start ,要求為執行 node index.js,所以接下來在 cmder 內輸入`npm run start`,即可執行 node index.js。
#### 常見 scripts 應用
再下載專案下來之後,有些時候需要清除檔案或是新增任何東西時,都可以先進來 .json 檔案內,在 scripts 內新增一個 key,並在元素內新增多個指令,即可在 cmder 內一鍵完成所有指令。
#### 補充說明
.json 檔案內都是物件形式,但內部的 key 較特別的地方在於說需要使用雙引號括起來。
## 3. Yarn ─ 與 npm相似的另一個套件管理工具
[Yarn 官網](https://yarnpkg.com/en/),由 Facebook 開發而成。
### 相關語法對照
#### 下載
`npm install` → `yarn`
#### 初始化
`npm init` → `yarn init`
#### 下載套件
`npm install left-pad --saved` → `yarn add left-pad`
#### 快速執行命令
`npm run start` → `yarn run start`
## 4. Jest 測試語法
[Jest](https://jestjs.io/docs/en/getting-started) 是一個協助測試程式資料的套件。
### 使用說明
大體使用方向與過去模板輸出及輸入使用類似,Jest 有自己的適用語法,以下會進行說明。
一般測試的檔名都是使用 index.test.js。
#### 測試資料語法
一樣在輸出端輸入 `module.exports`,在輸入端輸入`require()`外,外層建議包一個 describe 的函式,並在參數一放入要測試的函式名稱,參數二放入 test 的函式,第一個參數寫這個測試描述,第二個參數放入測試的函示,呈現較為完整的結構。
```Javascript=
let repeat = require('./index.js');
describe ('測試 repeat ', () => {
test('a 重複五次應該要等於 aaaaa', function() {
expect(repeat('a',5)).toBe('aaaaa');
});
test('p;K 重複三次應該要等於 p;Kp;Kp;K', function() {
expect(repeat('p;K',3)).toBe('p;Kp;Kp;K');
});
test('1Q! 重複零次應該要等於 ', function() {
expect(repeat('1Q!',0).toBe(' ');
});// 盡量多準備這種邊際測資
});
```
#### 執行測資
##### 方法一
更新 package.json 將 scripts 去執行 jest。
```javascript=
scripts: {
"test": "jest"
}
```
在 cmder 內執行:`npm run test`,即可完成測資。
##### 方法二
輸入`npx jest + 欲測試檔案名稱`
#### Test Driven Development 測試驅動開發
先寫測試後寫涵式
## 5. ECMAScript 2015 (ES6)
更多 ES6 語法,[詳見此](https://github.com/DrkSephy/es6-cheatsheet)。以下簡單介紹幾個常見的 ES6 語法。
### 變數宣告方式
#### let
變數只能在作用域當中使用,之後可被更動。
#### const ( constant 常數 )
宣告一個之後無法被更動的值。
### 字串拼接
#### 用「``」取代「''」
因為 ES5 語法內在多行拼接的時候需要再加上換行,換成「``」就可以直接進行多行拼接。
#### 用 `${}` 放入欲加入變數
透過大括號可直接加入變數。
```javascript=
function sayHi(name, age) {
console.log(`Hi, my name is ${name.toUpperCase()}.
I'am ${age} years old.
Nice to meet you.`)
}
sayHi('Nick', 25)
/*expected answer:
Hi, my name is NICK.
I'am 25 years old.
Nice to meet you.
/*
```
### 解構 Distructuring
透過解構讓陣列及物件更簡單的被呼叫及執行。更多請看[此](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment)。
#### 陣列
`let [元素名稱1, 元素名稱2, 元素名稱3] = 原本的陣列名稱`
```javascript=
let arr = ['apple', 'banana', 'orange'];
let [a, b] = arr;
console.log(b);
//expected answer : banana
```
#### 物件
解構內的物件 key 名稱需與原物件 key 名稱相同。
`let {物件當中的key1, key2} = 物件名稱`
```javascript=
let fruitPrice = {
apple: 20,
banana: 15,
orange: 25
};
let {apple, banana, orange} = fruitPrice;
console.log(banana);
// expected answer : 15
```
### 展開 Spread Operator
將陣列或物件當中的元素去除陣列及物件的框架,將元素複製到新的陣列或物件中。更多請看[此](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)。
#### 使用方式
##### 陣列
`... + 欲複製的陣列名稱` (可放在陣列中的任一位置)
```javascript=
let fruit = ['apple', 'banana', 'orange'];
let other = ['pear', 'grape', 'lemon', fruit];
let other1 = ['pear', 'grape', 'lemon', ...fruit];
console.log(other);
//expected answer : [ 'pear', 'grape', 'lemon', [ 'apple', 'banana', 'orange' ] ]
console.log(other1);
//expected answer : [ 'pear', 'grape', 'lemon', 'apple', 'banana', 'orange' ]
```
##### 物件
`... + 欲複製的物件名稱` (可放在物件中的任一位置)
```javascript=
let fruitPrice = {apple: 25,
banana: 15,
orange: 20
};
let otherFruitPrice = {pear: 30,
grape: 35,
lemon: 10,
fruitPrice
};
let otherFruitPrice1 = {pear: 30,
...fruitPrice,
grape: 35,
lemon: 10
};
console.log(otherFruitPrice);
/*expected answer:
{ pear: 30,
grape: 35,
lemon: 10,
fruitPrice: { apple: 25, banana: 15, orange: 20 } }
*/
console.log(otherFruitPrice1);
/*expected answer:
{ pear: 30,
apple: 25,
banana: 15,
orange: 20,
grape: 35,
lemon: 10 }
*/
```
#### 記憶體存放位置
因為展開比較像是複製的概念,所以說兩者記憶體內容存放位置不一樣,只是存放內容一樣而已。
而過去的直接將陣列或物件放進來時,這樣就會指向同一個記憶體位置,所以存放位置會一樣。
##### 陣列
```javascript=
let fruit = ['apple', 'banana', 'orange'];
let otherFruit = ['pear', 'grape', 'lemon', fruit];
let otherFruit1 = [...fruit];
console.log(otherFruit[3] === fruit);
//answer:true
console.log(otherFruit1 === fruit);
//answer:false
```
##### 物件
```javascript=
let otherFruitPrice = {pear: 30,
grape: 35,
lemon: 10,
fruitPrice
};
let otherFruitPrice1 = {
...fruitPrice
};
console.log(otherFruitPrice.fruitPrice === fruitPrice);
//expected answer: true
console.log(otherFruitPrice1 === fruitPrice);
//expected answer: false
```
### 反向展開 Rest Parameters
這邊常跟解構共同使用,如果不需要一個一個透過解構定義的話,可以透過 Rest Parameters 將剩餘的元素全放入解構的陣列或物件中。呼叫 Rest Parameters 時也會一次呼叫到所有剩餘被放入的元素。
需特別注意的是,Rest Parameters 只能放在陣列、物件或涵式中的最後一個,不能夾在中間。更多請看[此](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/rest_parameters)。
#### 陣列
此案例中的 Rest Parameters 取名為 other。
```javascript=
let fruit = ['apple', 'banana', 'orange'];
let otherFruit = ['lemon','grape', ...fruit];
let [l, ...other] = otherFruit;
console.log(otherFruit);
//answer:[ 'lemon', 'grape', 'apple', 'banana', 'orange' ]
console.log(l);
//answer:lemon
console.log(other);
//answer:[ 'grape', 'apple', 'banana', 'orange' ]
```
#### 物件
此案例中的 Rest Parameters 取名為 rest。
```javascript=
let fruitPrice = {
apple: 10,
banana: 15,
orange: 20
};
let fruitPrice1 = {
grape: 50,
...fruitPrice,
pear: 25
};
let {grape, ...rest} = fruitPrice1
console.log(fruitPrice1);
//answer: { grape: 50, apple: 10, banana: 15, orange: 20, pear: 25 }
console.log(grape);
//answer: 50
console.log(rest);
//answer: { apple: 10, banana: 15, orange: 20, pear: 25 }
```
#### 函式
此案例中的 Rest Parameters 取名為 numbs。
```javascript=
function multiple(a, b, ...numbs) {
return a * b * numbs[1]
}
console.log(multiple(2, 3, 4, 5, 6));
//answer: 2 * 3 * 5 = 30
```
### 預設值 Default Parameters
語法:`__=__`(加個等號)
在參數上先加入預設值,如果再使用陣列和物件時沒有放入元素、使用函式時沒有放入引數,那樣電腦就會依照先前所設的預設值跑出結果。更多[詳見此](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters)。
#### 陣列
```javascript=
let fruit = ['apple', 'banana', 'orange'];
let [a, b, o, g = 'grape'] = fruit; //g 預設值為'grape'
console.log(b); //answer:banana
console.log(g); //answer:grape
```
#### 物件
```javascript=
let fruitPrice = {
apple: 10,
banana: 15,
orange: 20
};
let {apple, banana = 20, grape = 40} = fruitPrice
// banana 預設值為 20,grape 預設值為 40
console.log(apple); //answer:10
console.log(banana); //answer:15
console.log(grape); //answer:40
```
#### 函式
```javascript=
function multiple(a, b = 1, c = 5) { // b 預設值為 1,c 預設值為 5
return a * b * c
}
console.log(multiple(2, 3)); //answer: 2 * 3 * 5 = 30
```
### 箭頭函式
以 => 符號簡化函式的語法,是 Syntax Sugar 的其中之一,在長串語法中可以讓語意的易讀性更高。更多說明詳見[Mozilla](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions)
```javascript=
let arr = [1, 2, 3, 4, 5]
console.log(
arr
.filter(function(value) {
return value > 1
})
.map(function(value) {
return value * 2
})
)
// 以上是一般的寫法,以下改成箭頭函式
arr
.filter((value) => {
return value > 1
})
.map((value) => {
return value * 2
})
// 當 function 內只有一個參數的時候,括號又可以被省略掉
// function 內直接 return 東西時,大括號可以被省略掉
// 所以可以精簡成以下的兩行
arr
.filter(value => value > 1)
.map(value => value * 2)
```
以上的語法上,上下兩種語法呈現的效果相同,但下方的箭頭函式可快速呈現簡易且易讀的語法。但如果說在一個語法內需要包含多個條件及程式時,還是建議寫一般函式。
### 輸出與輸入 Export and Import
#### 輸出端
直接在欲輸出的函式前加入`export`。
```Javascript=
function double(n) {
return n * 2
}
var PI = 3.1415926
module.exports = {
double: double,
PI: PI
};
```
以上是 ES5 的語法,以下是 ES6 的語法,並結合箭頭函式。
```Javascript=
export let double => n * 2
export const PI = 3.1415926
```
也可以使用
```Javascript=
let double => n * 2
const PI = 3.1415926
export {
double,
PI
}
//在大括弧中可以加上 = 重新命名,例:double = multipleTwo,在輸入端時就可以 multipleTwo 呼叫
```
#### 輸入端
`import {欲輸出的東西名稱} from '文件名稱'`
```Javascript=
var abc = require('./index');
console.log(abc.PI, abc.double(4));
//answer: 3.1415926 8
```
以上是 ES5 的語法,以下為 ES6 的語法。
```Javascript=
import {double, PI} from './index';
console.log(double(2), PI);
//answer: 4 3.1415926
```
除此之外,可以一次將所有語法以 * 引入,並用 as 取名。
```Javascript=
import * as cool from './index';
console.log(cool.double(2), cool.PI);
//answer: 4 3.1415926
```
或者是
```Javascript=
import {double = multipleTwo, PI} from './index';
console.log(multipleTwo(2), PI);
//answer: 4 3.1415926
```
#### 加入預設值 default 用法
##### 輸出端
```Javascript=
export const default double => n * 2
//預設輸出就是輸出 double 這個函式
export const PI = 3.1415926
```
##### 輸出端
```Javascript=
import double, {PI} from './index';
// 預設值不需要加大括號
console.log(double(2), PI);
//answer: 4 3.1415926
```
### Babel 安裝與使用
執行以上的 ES6 語法,部分內容需要透過 Babel 才可以順利執行,這邊是[官方](https://babeljs.io/docs/en/next/babel-node.html)的安裝說明,及簡易使用步驟。
#### 簡易步驟
1. 安裝必要套件:npm install --save-dev @babel/core @babel/node @babel/preset-env
2. 新增 .babelrc 檔案
3. 在此檔案填入以下內容(告訴 babel 要用這個 preset):
```Javascript=
{
"presets": ["@babel/preset-env"]
}
```
4. 在 CMDer 上執行 npx babel-node + 檔案名稱即可執行 ES6 語法。