# [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