# [JS102] 升級你的 JavaScript 技能:ES6 + npm + Jest
###### Date : 2021 Apr. 26 - 2021 Apr. 27
#### 模組化 & Library
- require
把功能切分開來,方便之後維護
> index.js
```javascript
// node 原生 os 套件
var os = require('os')
console.log(os.platform())
```
- export
>myModule.js
```javascript
function double(n) {
return n * 2
}
// 將寫好的 function 打包成 obj
var obj = {
double: double,
triple: function(n) {
return n * 3
},
}
// 將 obj 輸出出去
// module.exports 也可以輸出 number, string, array ...等
module.exports = obj
// 另一種輸出方法,但此種方法僅限輸出 object
exports.double = double
exports.triple = triple
```
>index.js
```javascript
// 引入 myModule , './' 表示當前目錄
var myModule = require('./myModule')
console.log(myModule.double(2), myModule.triple(10))
// 6, 30
```
#### NPM : Node Package Manager
- npm 安裝 & 使用套件
> command line
```shell
$ npm install left-pad
## 安裝套件
$ npm init
## 初始化 NPM ,並產生 package.json
$ npm install left-pad --save
## 安裝套件並儲存套件資訊到 package.json 的 "dependencies"
$ npm install left-pad --save-dev
## 安裝套件並儲存套件資訊到 package.json 的 "devDependencies"
$ npm install
## 安裝 package.json 裡的所有套件
## commit 的時候,記得把 node_module 資料夾排除
## 本地端要用的時候,再用 npm install 安裝
## 補充資訊:
## 從 npm 5 以後,--save 就已經變成預設的選項了,因此 npm install 不加 --save 也是可以的喔,一樣會把資訊寫到 package.json 裡面去!
```
> index.js
```javascript
var leftPad = require('left-pad')
console.log(leftPad(123, 10, '0'))
// 0000000123
```
- NPM Script
> package.json
```json
"script" : {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"hello": "echo hello world"
},
```
> command line
```shell
$ npm run start
$ npm run hello
## hello world
```
- yarn | [yarn 官網](https://classic.yarnpkg.com/en/)
跟 npm 87% 像,由 facebook 開發,語法稍簡單、速度較快,也很多人使用,用 npm & yarn 都可以。
#### 寫測試,單元測試 Unit Test
- 最陽春的方法,console.log
```javascript
console.log( 'output' === 'result' )
```
- Jest | [Jest 官網](https://jestjs.io/)
> command line
```shell
$ yarn add --dev jest
## 安裝 jest
$ jest
## 測試所有專案資料夾下的 *.test.js 檔
$ jest index.test.js
## 錯誤 : command not found: jest
## 因為 jest 沒有安裝在 global 環境下
*在 package.json 裡的 scripts 裡增加 "test": "jest index.test.js",
$ npm run test
## 將 jest 寫在 package.json 裡就可以執行了,或是使用以下 npx 方法
$ npx jest index.test.js
## 新版的 npm 支援,在專案資料夾下找到 jest 這個東西
```
> index.js
```javascript
function myFunction(input){
// code
}
module.exports = myFunction
// 一定要 export 出去才可以測試
```
> index.test.js
```javascript
var myFunction = require('./index')
// 使用 describe 群組化 test 資料,非必要但比較好看
describe('測試 myFunction', function() {
test('測試 1 應該要等於 輸出1', function() {
expect(myFunction(INPUT1).toBe('OUTPUT1'))
})
test('測試 2 應該要等於 輸出2', function() {
expect(myFunction(INPUT2).toBe('OUTPUT2'))
})
test('測試 3 應該要等於 輸出3', function() {
expect(myFunction(INPUT3).toBe('OUTPUT3'))
})
})
```
- TDD : Test-drive Development ( 測試驅動開發 )
先寫測試 &rArr 再寫程式
測試資料盡可能完整一點,邊測試邊開發
#### ECMAScript2015 (ES6) | [ECMA 規格書](https://www.ecma-international.org/publications-and-standards/standards/ecma-262/)
- var v.s. let & const
const 宣告的變數不可以重新賦職
var 的 Scope 是以 function 為單位
let 的 Scope 是以 block `{}` 為單位
- Template Literals ( 字串模板 )
```javascript
let name = 'ben'
// 不用再用 + 拼接
console.log(`hi ${name}`)
// hi ben
```
- Destructuring ( 解構 )
```javascript
// destructur array
const arr = [1, 2, 3, 4]
let [first, second] = arr
console.log(first, second) // 1, 2
// destructur object
const obj = {
name: 'nick',
age: 30,
address: 'taiwan',
}
let {address} = obj
console.log(address) // taiwan
// destructur double object
const obj = {
name: 'nick',
age: 30,
address: 'taiwan',
family: {
father: 'adam',
}
}
let {family:{father}} = obj
console.log(father) // adam
// destructur function
function ({a, b}){
console.log(a)
}
test({
a: 1,
b: 2
})
// 1
```
- Spread Operator ( 展開運算子 )
```javascript
// spread array
let arr1 = [2, 3, 4]
let arr2 = [1, ...arr1, 5]
console.log(arr2)
// [1, 2, 3, 4, 5]
// spread array in function
function add(a, b, c){
return a + b + c
}
let arr = [1, 2, 3]
console.log(add(...arr))
// 6
```
> `...` 無法巢狀展開
- Rest Parameters ( 剩餘參數 )
```javascript
let arr = [1, 2, 3, 4]
let [first, ...rest] = arr
conosle.log(rest) // [2, 3, 4]
let obj = {
a: 1,
b: 2,
c: 3,
}
let {a, ...obj2} = obj
console.log(a, obj2) // 1, {b: 2, c: 3}
```
> `...rest` 只能放最後面
- Default Parameters ( 預設值 )
```javascript
function repeat(str, times = 2) {
return str.repeat(times)
}
console.log('abc') // abcabc
```
- Arrow function ( 箭頭函式 )
```javascript
const double = n => return n*2
```
- Import & Export
> util.js
```javascript
export function add(a, b) {
return a + b
}
export const Pi = 3.1415926535
// 也可以這樣寫
export {
add as addFunction,
PI
}
// 或是這樣寫
export default function add(a, b) {
return a + b
}
// imorpt 就不用大括號
import add from './utils'
```
> index.js
```javascript
import {add, PI} form './utils'
console.log(add(3 + 5), PI) // 8, 3.1415
// 也可以這樣寫
import {addFunction as a , PI} form './utils'
console.log(a(3 + 5), PI) // 8, 3.1415
// 或是這樣寫
import * as utils from './utils'
console.log(utils.addFunction(3, 5), PI) // 8, 3.1415
```
> terninal
```shell
## 如果 node.js 不支援 ES6 語法,使用 babel
$ npx babel-node index.js
```
- Babel | [Babel 官網](https://babeljs.io/)
將 ES6/7/8 => ES5 等等
:::info
Babel 的安裝說明:https://babeljs.io/docs/en/next/babel-node.html
設定步驟:
安裝必要套件:npm install --save-dev @babel/core @babel/node @babel/preset-env
新增 .babelrc
填入內容,告訴 babel 要用這個 preset:
{
"presets": ["@babel/preset-env"]
}
最後使用 npx babel-node index.js 即可
:::
#### Ref.
- [如何看待 Azer Koçulu 刪除了自己的所有 npm 庫?](https://www.zhihu.com/question/41694868)
- [抽掉 11 行程式就讓網路大崩塌!一場撞名事件,看開源的威力與權力衝突。](https://www.inside.com.tw/article/6041-how-one-programmer-broke-the-internet-by-deleting-a-tiny-piece-of-code)
- [es6-cheatsheet](https://github.com/DrkSephy/es6-cheatsheet)
---
##### 心得
模組化真的很方便,雖然還沒真的遇到覺得方便的時候,因為我現在寫的 code 大多也不超過 30 行,如果你跟我一樣有看超過 10 行 code 就會死的病,就會體會到模組化的威力了😂😂😂
NPM 固然方便,但是在 left-pad 這個例子上, Azer Koçulu 刪除了自己的所有 npm 庫,可以發現 dependencies 的問題,網路開源的方便性,也顯示了人的惰性,Huli 的學生絕對可以自己寫出來 left-pad 這個套件,但同時我也相信寫不出來的大有人在🤔,不然不會造成這麼大的波及。
在我上這門課之前,都有聽說要寫測試、寫測試,但甚麼是測試?還有測試的重要性?原來這就是測試,以前只會陽春的 console.log 現在多了 Jest 這個測試 framework 還有 TDD 這個開發方法感覺又升級了。
最後 ES6 也是扎實的教好教滿,當然沒有全教,因為太多了太深了,講不完,但現在這樣就夠用了,先熟練這些好用的新武器吧。