1、Closure 閉包

Closure是什麼?我們以setTimeout舉例
Ps. 完整的 code 可以看closure那邊文章

假設今天想要印出 1 -> 2 -> 3 (每過一秒印出 1++)
但是發現用var宣告變數後,結果跟預想的不太一樣

> for(var i = 0; i < 3; i++) {
>     setTimeout(()=>{
>         console.log(i)        # 3 -> 3 -> 3 (每隔一秒印出一個3)
>     }, 1000 * i)
> }


把上面 for 迴圈中的 var 改成 let 做到 1 -> 2 -> 3 (每隔一秒印出一個數字) 為什麼改成 let 就可以執行,是因為閉包的概念
var的話就算離開for迴圈還可以存活
所以最後 i 就會讀取 迴圈最後結束的值

如果改成let的話,離開迴圈就無法存活,所以會有一個閉包把這個i一起包進webAPI
之後 setTimeout 的 i 就會依照閉包中的i給值

1-1、function都會有Closure的特性

(1) JS再執行的過程中,會去考慮非同步相關的函式 or function,是否有用for迴圈包住,用let宣告的話,裡面的 i 會持續變動
(2) let原本只會存活在for回圈裡面,但是被setTimeout一起帶去webAPI裡面,所以等 stack 裡面全部執行完後, let才會依然存活
(3) 他會去捕捉周圍的環境變數,但是只會去抓自己有用到的東西

1-2、Closure實際範例

先做一個fc,裡面包一個變數、fc

> function hi() {
>     let a = 1
>     function hey() {
>         console.log(a)      # 1   -> 會印出1
>     }
>     hey()
> }
> hi()

> Ps. let a 在 fc 存活,所以印出來很正常

現在我們換掉一些code,並在 fc 外面試著把 a 印出來

> function hi() {
>     let a = 1
>     function hey() {
>         console.log(a)  4. 印出 1
>     }
>     return hey          1. 我們把fc hey傳出去(這時候封包會一起把let a傳出去)    
> }
> 
> const newHi = hi()      2. 並用一個新變數接住,這個newHi是一個fc喔!!
> newHi()                 3. 呼叫newHi

> Ps. 正常的情況下,let應該會被限制在function才對,不過因為封包的關係
>     return hey 的時候,會做一個閉包把要用的參數傳出去

2、遞迴函式

2-1、斐波那契數列

以斐波那契數列來示範遞迴函式
斐波那契數列 = 前兩個位數的數字相加等於新的位數數字

> function fib(n) {
>     if( n === 0 ) {               -> 到0停止
>         return 0
>     }
> 
>     if (n===1) {
>         return 1
>     }
> 
>     return fib(n-1) + fib(n-2)    -> 可以自己呼叫自己的原因,是因為有終止條件
> }
> 
> console.log(fib(10))


糖果題今天如果寫得太複雜,可以試試用遞迴方式來寫

3、IIFE = Immediately Invoked Function Expression (念 “ 一 ㄈㄧ “ )

寫套件的時候很常用到,因為不想讓自己的套件的fc被影響到

> 用一個小括弧把fc包住,並在最後用()呼叫此fc
> (function () {
>     console.log("123")
> })**()**;              -> 最後記得用()呼叫,可以帶參數進去喔
> 
> 這種寫法的目的,是因為只能使用一次,不會漏到外面

使用立即函式的好處,除了可以立即執行程式碼,省略多餘的呼叫,還可以用來避免汙染全域執行環境的東西,減少開發時因相同命名相衝的bug。


4、parcel 打包檔案

parcel是一個上線過程的“打包器”
如果今天太多JS、CS檔案,要在index.html連結其他檔案的時候,該怎麼辦?

> 現在有3個js檔案,我想把這三個壓成一個JS檔案 -> webpack、parcel可以做到
> <script src="app.js"></script>
> <script src="test.js"></script>
> <script src="house.js"></script>


市面上有很多打包工具,可以把一大堆的JS、css壓縮成一包小東西
npm -> 前端世界專門安裝套件的東西 (npm有點慢)
yarn -> facebook做的,誕生原因是因為 npm 有點慢
( yarn 比較快的原因,是因為yarn可以平行下載,一次下載多個檔案 )

4-1、打包器實際執行

一、在專案終端機輸入

> yarn init

會誕生一個json檔案 => package.json

> {
>   "name": "day17",
>   "version": "1.0.0",
>   "main": "index.js",
>   "license": "MIT",        -> 此專案的授權來源
> }


(1) MIT是麻省理工
(2) BSD是柏克萊
這兩間授權的話,基本上都可以免費使用
(3) GPL也是一種開源的受援,不過你今天用GPL的license寫一份專案,你那一份專案也要開源

二、接著在json檔上的scripts加一些code

> scripts -> 這個很常是前端工程師,加入自己很常用的語法

> "scripts": {
>     "hi": "echo 123",
>     "hey": "echo 456"
> }
> 
> 呼叫hey預設方法
> yarn run hey           # 456 

三、安裝 parcel

輸入後,會直接下載一大堆檔案,包在一個資料夾(node_modules)裡面

> yarn add --dev parcel 

並會在json檔生成一個多幾行程式

> "devDependencies": {
> "parcel": "^2.7.0"
> }

> dev是開發的意思 - devDependencies


如果不小心刪掉node_modules
再打上yarn install就會回覆

四、接著到.json檔的scripts裡面,新增一段
下面這一行的指令,就跟 rails s 一樣,就是啟動server

> "scripts": {
>     "dev": "yarn parcel index.html"  ->  加在這邊的關係,是因為我之後想重複只用 dev 這個指令
> },                                       要注意那個index.html,如果今天沒有把script包在index裡面
>                                          就要yarn parcel src="this_test.js"

五、接著直接呼叫

> yarn run dev

如果今天沒有加上dev那一條,可以直接這樣

> yarn run "yarn parcel index.html"

六、最後把type=”module” 寫進script裡面

> <script src="app.js" defer type="module"></script>

這樣寫後,開啟環境,並開後台會發現 scr: “一長串的姓名.js”
以前的時代,瀏覽器會進行快取,所以index 有為每個檔案取名一長串得符號,如果內容物改變的話,名字究會改變

5、JS import/export

es modules 如果今天想要引用別人的模組或是自己寫的模組

5-1、export模組輸出

5-1-1、方法一、輸入給別人用

> math.js

> export function add(a, b) {
>     return a + b
> }

5-1-2、方法二、另外一種export寫法

> math.js

> function add(a, b) {
>     return a + b
> }
> export { add, minus, multiple } -> 也可以在最後寫export,把所有想要傳出去的傳出去

5-2、import模組輸入

> app.js

> import{ add, PI } from "./math.js"  -> 這個add套件是寫在math.js裡面的
>                                     -> 如果今天有多個要import,可以,隔開就好
> console.log(add(1, 5))     # 6


如果今天沒有打包模組,系統會看不懂import、export

5-3、export default意思

export的時候,default是什麼意思?
default其實就是一個預設匯出的寫法!

> export default add -> export default 只會有一個   
> import X from "./math.js"   -> 輸入預設的模組,可以隨便取名使用   


輸入也可以全部加寫在一起
import X, { add, minus } from “./math.js”

5-4、引用別人的套件

> yarn add dayjs -> 這樣會把dayjs裝在node_modules裡面
> 
> import dd from "./dayjs" -> 直接取用就可以,可以隨意取名的原因,是因為對方的fc是default

如果匯入同個資料夾有相同的fc,再匯入的時候,記得用as

> import { add as myAdd } from "./math.js" 
> 
> function add() {
>     console.log(234)
> }

6、JavaScript 測試環境

到專案的終端機輸入以下

> yarn add -dev jest