# [JS102] 升級你的 JavaScript 技能:ES6 + npm + Jest
###### tags:`Frontend`
[TOC]
```
npm init
npm install something
```
如果在 init 的子資料夾做 install 的話,會自動往上層的 init 安裝。
如果希望子資料夾也有 init 的話,就下 `npm init`
## 設定 eslint
eslint = ECMAScript + lint
已經在 repository 先裝好了,會在 commit 之前做語法檢查,不想被檢查的話就用:`/*eslint-disable-next-line*/`
## 模組化 Module
把不同功能分成不同模組,分別管理使用,這樣就可以在不同檔案之間使用相同 function。
### 把模組拿過來使用
透過宣告一個變數 os 來使用 Module,`let os = require('OS')`
### ==把模組借給別人==
```javascript=
// CommonJS module
// module
function double () {}
function sum () {}
// 團體輸出
let obj = {
double: double, //function
sum: sum,
triple: function (n) {
return n*3
}
}
module.exports = obj
// 或是分別輸出 function (到空 object 裡面)
/*相當於
let obj = {
double: double, //function
}
*/
exports.double = double
```
```javascript=
let myModule = require('./myModule')
myModule.double(10)
```
### 常用的 module 通常會放在 utils裡面
## npm, Node Package Manager
> package = 套件, module, library...功能都差不多
(記得要先做 `npm init`)
`npm install left-pad --save`
* --save 會==紀錄專案使用到的 package== (紀錄在 dependency 裡面), name...一堆基礎資訊。
* ==從 npm 5 以後,--save 就已經變成預設的選項了==,因此 npm install 不加 --save 也是可以的,一樣會把資訊寫到 package.json 裡面去!
根據 package.json 裡面記錄的資料,專案給其他人使用的時候就不需要把專案使用到的一系列 module 給別人,讓對方自己去下載就好,`npm install`
### npm scripts
在 package.json 裡面的 scripts 放自己寫的腳本,自動執行一些事情。
`npm run start` 就可以執行了
例如:
![npm scripts
](https://i.imgur.com/csp5PBA.png)
### yarn:npm 以外的另一種選擇
## 幫你的程式寫測試
測試的時候要考慮到邊際效應**考edge case**
### 單元測試 Unit test
慣例是會在 index.test.js 這個副檔名.test.js裡面做測試,大致步驟是↓
1. reuire 要做測試的檔案,然後用 test( ) function.
```javascript=
const sum = require('./sum');
//./sum 記得要做 export
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```
* 如果相同的 test() 有很多組,可以一併放入 describe() 裡面
1. 把執行 jest 的步驟加入 package.json 的腳本裡面
```javascript=
{
"scripts": {
"test": "jest"
}
}
```
* `npm run test` 會自動去找副檔名是 .test.js 的檔案
* `jest index.test.js` //通常會找不到,因為 jest 不是 global
如果是比較新的 npm,會有內建的 npx 可以從專案裡面找出 jest 去執行
`npx jest index.test.js`
### jest 實作
```javascript=
// index.test.js
const challenge = require('./challenge')
test('1', () => {
expect(challenge.search([1,2,3,4,5,6,7,8,9,10],10)).toBe(9)
})
```
```javascript=
// challenge.js
function search (arr, n) {
return binarySearch (arr, n)
}
function binarySearch(arr, n) {
// code
}
exports.search = search
exports.binarySearch = binarySearch
```
### 測試驅動開發 TDD
> tes-driven development
先把測試寫好,再根據測試結果去做開發。
## ==ES6==
JavaScript 是根據 ==ECMAScript (是一個規範、標準)== 所實作出來的語言,ES6 = ES2015 (相當於新的版本)
## 宣告變數的新選擇:==let 與 const==
* const 是常數,不可以改它的值,對於 obj 也是同理,例如↓,a 存的是記憶體位址
```javascript=
const a = {
num: 10
}
a.num = 20
console.log(a) //20
```
scope 是作用域,變數的存在範圍
* var 的生存範圍是 function { }
* let, const 的生存範圍是 block { } //跟其他語言相似
## ==Template Literals==
把特定變數加到字串裡面,如此就不用慢慢用 加號+、單引號''之類的方式來串接變數與字串。
```javascript=
console.log(`hello, ${name}`)
```
## ==Destructuring==:解構
過去要把 arr, obj 的 element, value 取出來的話,需要在額外宣告變數來儲存這些值,例如:
![Destructuring](https://i.imgur.com/K5h4Nz6.png)
使用解構之後,可以用一句話就完成「宣告變數跟儲存目標的值」,例如:
![Destructuring](https://i.imgur.com/JJdateC.png)
* 需要注意的是對 obj 做解構的話,宣告的變數名稱要跟 obj 的 key 名稱相同
* 對 arr 做解構的話,宣告告的變數名稱要跟 arr 的 index 對稱
```javascript=
let arr = [1,2,3,4,5]
let arr1 = arr[0]
let a, b, c
[a, b, c] = arr
// a = 1, b = 2, c = 3
```
## 把東西展開:==Spread Operator==
`...` Spread Operator 會把 arr, obj 裡面的值取出來,這個方法傳的是**值**而不是**reference**,例如:
* arr = [1,2,3] 展開之後會得到 1, 2, 3 這些 value
![](https://i.imgur.com/HdktqJ0.png)
* obj = {a:1, b:2} 展開之後會得到 a:1, b:2
![](https://i.imgur.com/CjFKjci.png)
## 只展開一部分:==Rest Parameters==
跟展開的功能很像,`...rest`(也可以叫其他名稱),只對一部分的 arr, obj 進行==集合==,例如:
```javascript=
[a, b, ...rest] = [10, 20, 30, 40, 50];
console.log(rest); //[30,40,50]
```
## 加上預設值:==Default Parameters==
直接在宣告 function 的時候,給 parameter 加上預設值,或是在解構 deconstructor 的時候加上預設值。
![parameter](https://i.imgur.com/TvBPYSK.png)
![deconstructor](https://i.imgur.com/TQ3KL4T.png)
## ==箭頭函式== Arrow Function
宣告 function 的新方法,理想上可以把 function, ( ), { } 都省略掉增加可讀性,例如:
![](https://i.imgur.com/A5acxxy.png)
## Import 與 Export
package 在ES6 之前的使用方式如下
```javascript=
function sum () {}
module.exports = sum //把 package 丟出去
const sum = require("sum") //把 package 拿進來
```
相當於,
```javascript=
export function sum () {} //把 package 丟出去
import {sum} from "./utils" //把 package 拿進來
```
或者是,
```javascript=
function sum () {}
export {
sum
//或者是用新名稱輸出 sum,例如 sum as sumFunction
}
```
* 原生 node 還沒有支援 export / import 的話,使用` npx babel-node index.js`
* 如果要把 Fn 改成別的名稱,可以用 `as`,
### 想把全部 package 都抓進來,則用:
```java csript=
import * as utils123 from './utils'
```
```java csript=
export default function sum () {} //把 package 丟出去
import {default as sum} from './utils' //把 package 拿進來
//或者是
import sum from './utils'
```
影片下有補充
## Babel : JS compiler
把新語法轉換成舊語法的工具,為了要支援不同版本的瀏覽器。
安裝 babel 之後的設定步驟:
1. `npm install --save-dev @babel/core @babel/node @babel/preset-env`
1. 新增 .babelrc (放在專案根目錄底下)
1. .babelrc 填入內容,告訴 babel 要用這個 preset:
```javascript=
{
"presets": ["@babel/preset-env"]
}
```
4. 最後使用 `npx babel-node index.js` 即可
[更多 ES6 新增的語法](https://github.com/DrkSephy/es6-cheatsheet)
[補充](https://blog.huli.tw/2020/01/21/webpack-newbie-tutorial/)
## 補充的筆記
「在 ES6 出現以前,JavaScript 並沒有一個標準的模組化規範。Node.js 支援 ==CommonJS==,所以才可以用require跟module.exports,但是瀏覽器原生沒有支援,所以才需要像是 browserify 以及 webpack 這種工具」
browserify, webpack 它們的原理用極簡的方式來說就是:把要引入的 utils 裡面的 fn,直接到到主要檔案(被引入的檔案),然後用寫出一些 fn 來達成require在做的事。
webpack 這個技術出現以前是什麼樣子?
Nodejs 和瀏覽器分別是不同的執行環境,Nodejs有支援的語法或規範,瀏覽器不一定也有支援,反之亦然。在 Nodejs 裡面使用 require/module.export (CommonJS,一個規範)的時候,瀏覽器並沒有支援,這時候就要自己寫工具來支援了。
那時候碰到什麼樣的問題?
不支援或支援度差的第三方模組,在瀏覽器不好引入,但模組化的概念又很方便,所以要自己寫程式法(AKA工具)來完成這件事。
這個技術的出現如何解決問題?
webpack 定義了許多 loader,可以直接整合各種模組然後輸出成單一檔案(瀏覽器看得懂)
所以這項技術應該如何使用?
打包模組和主要的JS檔案,一鍵輸出大禮包JS的功能
跟以前的解法比起來,差別在哪裡?有什麼優缺點?
很方便,CSS、圖片檔...任何東西都可以被Webpack視為模組然後引入
缺點的話...大家都用習慣了,不太知道背後原理和歷史故事(X