# 2024-06-28 JavaScript ## 參數和引數的定義 ```js function hi(a, b) { // <=== a, b 屬於參數 } hi(1, 2); // <=== 1, 2 屬於引數 ``` ## 回傳值 ```js function add(a, b) { console.log(a + b); //console.log只是過程,不是結果 return a + b; //真正回傳值需要使用return } add(1, 2); ``` [情境一] ```js function add(a, b) { return a + b; } console.log(add(1, 2)); //最後結果: // 3 ``` [情境二] ```js function add(a, b) { console.log(a + b); } console.log(add(1, 2)); // 1. add(1,2)會被執行,執行過程印出 3,但是沒有return,所以會回傳undefined // 2. console.log(add(1,2)) => console.log(undefined) // 3. 最後印出undefined //最後結果: // 3 // undefined ``` [情境三] ```js function add(a, b) { return console.log(a + b); } console.log(add(1, 2)); // 1. add(1,2)會被執行,遇到console.log(a + b),會得到3 // 2. 因為console.log不會回傳值,所以會得到undefined,最後return會回傳undefined // 3. console.log(add(1,2)) => console.log(undefined) // 4. 最後印出undefined //最後結果: // 3 // undefined ``` [情境四] Early Return (不寫else相對輕鬆,因為不用處理兩條判斷式) ```js function is_adult(age) { if (age >= 18) { // 可以把重點放在這一塊 return true; } return false; } console.log(is_adult(20)); ``` ## What 什麼是函數function? - 輸入值 與 輸出值之間的關係 - 函數名字 = 這個關係的名字 ## Why 為什麼要使用函數function? - 可重複利用 - 給程式碼意義 ## 電腦界兩大難題 - 命名 naming (書籍:naming things) - 快取 cache(暫存) => 不知道什麼時候該放手 ## 函數 function - 什麼是一等公民? 在有階層區分的最低等,最普通 - fn = 一等公民,一級物件(first-class citizen) - 把 fn 當作和一般物件來看,可以丟來丟去,跟其它 整數,字串..一樣 - 123, "aaa", [], {} 都是一等公民 - fn 與其它人的差異就是,可以被"呼叫",在必要時呼叫 ``` ※ 函數沒有小括號就是沒有啟動,當作物件來丟 ``` [情境一] ```js function hi(a) { console.log(a); } function hey() { console.log("hey"); } hi(hey); // hey是物件 沒有小括號沒有執行 // 因為hey()沒有return,所以會回傳undefined // 會把結果undefined丟進hi => hi(undefined) hi(hey()); ``` ![image](https://hackmd.io/_uploads/SyoqgZhIA.png) [情境二] ```js function hi(a) { // a參數會收到 hey 物件 a(); // 把 hey 加上小括號() 表示執行物件a (hey) } function hey() { console.log("hey"); } hi(hey); // 帶入物件hey ,hey是物件 沒有小括號沒有執行 最後結果: hey ``` [情境三] ```js function hi(a) { a(); } const hey = function () { console.log("hey"); }; // 會先執行hey()得到hey,但回傳值會是undefined // 再把undefined帶入hi(),因為對undefined發動,就會出錯 hi(hey()); 最後結果: 如下圖 ``` ![image](https://hackmd.io/_uploads/HJhhfZhL0.png) [情境四] ```js function hi(a) { a(); } const hey = function () { console.log("hey"); }; // 會先執行hey()得到hey,但回傳值會是undefined // 再把undefined帶入hi(),因為對undefined發動,就會出錯 hi(hey()); 最後結果: 如下圖 ``` [情境五] ```js function hi(a) { a(); } function hey() { function inner() { console.log("inner!!"); } return inner; } hi(hey()); // 會先執行hey(),內部因為有return inner,所以會回傳inner物件 // 然後當作hi function的引數帶入 => hi(inner funciton) // a = inner funciton,然後在hi function內啟動a() => inner() 最後結果: inner!! ``` [情境六] ```js function hi(a) { a(); } function hey() { return inner; <== 因為變數提升關係,所以會回傳inner function //第一階段就會變數提升 function inner() { console.log("inner!!"); } } hi(hey()); // 會先執行hey(),內部因為有return inner,所以會回傳inner物件 // 然後當作hi function的引數帶入 => hi(inner funciton) // a = inner funciton,然後在hi function內啟動a() => inner() ``` [情境七] ```js function hi(a) { a(); } function hey() { // 高階函數 function inner() { // 不是高階函數,因為沒參數,也沒return console.log("inner!!"); } return inner; } hi(hey); // 高階函數 // 把hey丟入hi funtion,a = hey,然後再啟動a() => hey() // 啟動hey()因為有return所以會得到inner function,但沒有加上()去啟動 // 但是沒有輸出,所以結果是空白 最後結果: 空白,沒輸出結果 ``` ## 高階函數 (higher order function) - 吃別人的function當參數 - 回傳 function - 只要符合以上兩個條件之一都屬於高階函數 ## Callback function (回呼) - 不用等待,直到他自己做完,自動回傳結果 ## 陣列 - 為什麼陣列從 0 開始算? => 索引值=偏移數量,索引值就是往旁邊移動幾格,幾格就是偏移數量 [情境一] ```js const list = [1, 2, 3, 4]; for (let i = 0; i < 4; i++) { list[i] = list[i] * 2; } console.log(list); // for, [i] // -> [2, 4, 6, 8] // 不是好的做法: 是因為會改變原本的陣列內容,可新增一新的陣列來儲存 ``` 修改寫法 ```js const list = [1, 2, 3, 4]; const newList = []; for (let i = 0; i < 4; i++) { const value = list[i] * 2; newList.push(value); } console.log(list); console.log(newList); ``` ![image](https://hackmd.io/_uploads/SkZv_-hLA.png) ### NaN (面試題會問的東西) ``` 1. NaN 他是數字 用來表示不是數字 2. 1 / 0 ⇒ Infinity, 0 是一個趨近於 0 的數字 3. Infinity 是一種number型別 typeof Infinity (是一種概念) 4. NaN 是一種number型別 typeof NaN 5. 是根據IEEE754規定 6. NaN === NaN ⇒ false NaN不會等於任何東西 包括他自己 (IEEE754規定) ``` 利用NaN的特性之一 來判斷輸入的數值是不是NaN (使用JS內建,不用自己寫) ```js function isNaN(value) { return value !== value; // 利用 NaN 人設,自己不會等於自己 } console.log(isNaN(NaN)); ``` Infinity + Infinity ⇒ Infinity Infinity - Infinity ⇒ NaN ### 使用map (對現有的東西,做一件事情,然後回傳新陣列) [情境一] ```js const list = [1, 2, 3, 4]; const newArray = list.map(function (number) { return number * 2; }); console.log(newArray); // [2, 4, 6, 8] ``` ![image](https://hackmd.io/_uploads/HkjTuNhUA.png) [情境二] ```js const list = [1, 2, 3, 4]; const newArray = list.map(function (number) { number * 2; // 沒有return }); console.log(newArray); ``` ![image](https://hackmd.io/_uploads/Sy__KEnIC.png) [情境三] ```js const list = [1, 2, 3, 4]; // 函數變物件的好處 const double = (x) => x * 2; const newArray = list.map(double); // 對於map來說 double就是callback function console.log(newArray); ``` [情境四] ```js const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const even = (n) => { return n % 2 == 0; // return false => [false, false, false, false, false, false, false, false, false, false] // return true = > [true, true, true, true, true, true, true, true, true, true] }; const newArray = list.map(even); // map判斷真假值,回傳個數一定都會一樣 console.log(newArray); ``` ![image](https://hackmd.io/_uploads/BkgCoN38C.png) ### 使用filter 過濾,回傳新陣列 [情境一] ```js const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // [2, 4, 6, 8, 10] const even = (n) => { return n % 2 == 0; // return false => [] // return true = > [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] }; const newArray = list.filter(even); console.log(newArray); ``` ![image](https://hackmd.io/_uploads/BJxfiN2UR.png) ### 使用reduce (回傳值 => 當作下一次累加值) [情境一] 有設定預設值0 ```js const list = [1, 2, 3, 4, 5]; // reduce 歸納 // 累加值acc 目前值cv => current value // 不一定只能是加法(+),也可以進行其它運算 const result = list.reduce(function (acc, cv) { return acc + cv; }, 0); // 0 為累加值初始值 console.log(result); // 15 ``` ![image](https://hackmd.io/_uploads/BJdC6Eh8R.png) [情境二] 有設定預設值0 ```js const list = [1, 2, 3, 4, 5]; const result = list.reduce(function (acc, cv) { return 1 + cv; <=== cv值會一值被替換,由1..5,最後return 1 + 5 }, 0); // 0 為累加值初始值 console.log(result); // 6 ``` [情境三] 沒有設定預設值 ```js const list = [1, 2, 3, 4, 5]; const result = list.reduce(function (acc, cv) { return acc + cv; }); // 沒有預設值,會拿陣列第一個元素當累加值,第二個元素當作第一輪目前值 console.log(result); // 15 ``` ![image](https://hackmd.io/_uploads/B1dSkrhL0.png) [情境四] 使用reduce求最大值,但是"不要自己寫",拿內建現成函數Math.max ```js const list = [1, 9, 10, 3, 5]; const result = list.reduce(function (a, b) { if (a > b) { return a; } return b; }); console.log(result); ``` ![image](https://hackmd.io/_uploads/SyhUGB28R.png) ``` [面試題] 泡泡排序法 bubble sort (演算法) ``` ### 排序sort (使用內建sort()會需要留意用法) ```js const list = [1, 9, 10, 3, 5]; const newList = list.sort(); console.log(newList); // 會轉換成字串來比較 最後結果: [1, 10, 3, 5] <=== 原因是因為內建sort會先將"數字"轉換成"字串"來比較 ``` ![image](https://hackmd.io/_uploads/BJxRXS2IR.png) [綜合練習題] ```js const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 所有奇數的平方和 // const oddList = list.filter(function (number) { // return number % 2 == 1; // }); // const squareList = oddList.map(function (number) { // return number * number; // }); // const total = squareList.reduce(function (sum, number) { // return sum + number; // }); const list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; const total = list .filter((n) => { return n % 2 == 1; }) .map((n) => { return n * n; }) .reduce((sum, cv) => { return sum + cv; }); console.log(total); // 165 ````