[TOC] ## Code review #1 by 季芳 ### q6 * 目前把問題寫死,所以問句都是"Please enter a name in English: ",使用者會不知道已經問到第幾個名字了 * 重複的動作可以包成小function * 且因為function有名字,也能讓程式碼更好讀 * for迴圈幾乎都可以用Array methods解決 * map, filter, reduce, from * reduce可以看阿傑的鐵人文章 * 有些人也會把各式各樣的驗證再包成一個function * 因為如果小驗證很多的時候就會太長 * 跟型別轉換有關的變數宣告可以少部份使用匈牙利命名 ### q8 * 忘記要用遞迴寫了!! * 可以試試看不同的切入點:轉字串後處理,或是直接用數字處理 ### q9 * 季芳也曾經被問如果陣列長度不一樣的話要怎麼處理,但可能可以不用涵蓋到這件事,畢竟這算是增加需求了 ### q10 * 找質數反證法 > 一個自然數若恰有兩個正因數(1及此數本身),則稱之為質數。 > 找質數反證法:找到該值的開根號並取餘數等於0就不是質數。 > 0,1不是質數,2是所有質數中唯一偶數。 * 如果for迴圈不好讀,但又不知道要怎麼重構,可以先拆成function ## Code review #2 by Jade ### q8 覺得沒什麼問題,命名命得很清楚 不過 test 你好像沒有放新命名的 function 所以會有錯XD ### q10 1. isPrime 沒有判斷如果是 1 的話 return false 或許是這樣你才在 Array.from element + 2 嗎? ```javascript const getNumbers = Array.from( Array(maxNumber - 1), (element, index) => index + 2 // 從2開始放進array ); ``` ```javascript // isPrime後來改成加入 if (number < 2) { throw new Error("請輸入大於1的整數"); } ``` 2. toSorted() 有點太新了,如果要用的話,專案要 pin node@20 這樣 install 才可以使用 - volta pin node@20 - [volta介紹](https://ithelp.ithome.com.tw/articles/10306738) - [volta加入node版本](https://docs.volta.sh/guide/understanding#managing-your-project) - 其實每個專案要用的版本應該都要放在 package.json 上,這樣要使用的人 install 的時候才不會有問題 - [直接改package.json](https://docs.npmjs.com/cli/v10/configuring-npm/package-json#engines) 4. 覺得你的程式已經很簡潔了,不過像是要拿 1~50 的數字把他裝起來一個變數會更語意化 ```javascript // 原本全部串在一起 const primeNumbers = Array.from( Array(maxNumber - 1), (element, index) => index + 2 ).filter(isPrime); // 改成 const getNumbers = Array.from( Array(maxNumber - 1), (element, index) => index + 2 ); const primeNumbers = getNumbers.filter(isPrime); ``` ## Code review #final by Chris ### q6 * function命名綁架參數 * 現在是流程設定和資料一起丟進去 * 流程控制跟資料輸入要分開,先記得這件事,現在可能還比較沒辦法體會 * 我目前的寫法看起來像pipe(流程),但第一個參數卻不是function,不一致 * getAnswersAndPrintResults的三個參數,很像在設定它 * getAnswersAndPrintResults應為副程式,跟main相關 * 因為我也沒有要測試他,所以不用分成別的模組 * 邏輯界面分離就好,不要為了包模組而分檔 ```javascript function main() { // 原本寫的 const questions = [ "Please enter the 1st name in English: ", "Please enter the 2nd name in English: ", "Please enter the 3rd name in English: ", "Please enter the 4th name in English: ", ]; getAnswersAndPrintResults( questions, // 丟了四個字串,像是丟了資料的感覺 validationsInQ6, oddLettersOfFirstAndThirdNames ); // 改成 // 用參數去命名(請見下方說明) askQuestionsAndDoSomething( { question: questions, validation: validationsInQ6 }, oddLettersOfFirstAndThirdNames ); } main(); ``` * 命名議題:什麼時候用參數命名、什麼時候用函式回傳的結果命名 - 當回傳值指派給某個變數時,函式可以命名為回傳的結果 - 否則這個函式就是一個「動作」,利用參數去做一些動作,所以應該要依照參數去命名 ```javascript // 將sin(90)這個「sin值」指派給a,所以sin這個函數命名為sin var a = sin(90); // 因此我的function如果有回傳值並指派給某個變數,才適合命名為getAnswersAndPrintResults const answer = getAnswersAndPrintResults(...) ``` #### getAnswersAndPrintResults * indexOfQuestion要當作getAnswers的參數傳入 * 寫法要pure,意思是函式不要影響到全域變數 * 寫法pure還有一個優點是把整塊function搬走會比較簡單 ```javascript // 原本寫法 export function getAnswersAndPrintResults(questions, validations, doSomething) { // ... let indexOfQuestion = 0; function getAnswers() { if (indexOfQuestion === questions.length) { // ... } else { rl.question(questions[indexOfQuestion], (answer) => { try { // ... indexOfQuestion++; getAnswers(); } catch (error) { // ... getAnswers(); } }); } } getAnswers(); } // 可以改成 export function getAnswersAndPrintResults(questions, validations, doSomething) { // ... function getAnswers(indexOfQuestion) { if (indexOfQuestion === questions.length) { // ... } else { rl.question(questions[indexOfQuestion], (answer) => { try { // ... getAnswers(indexOfQuestion + 1); } catch (error) { // ... getAnswers(indexOfQuestion); } }); } } getAnswers(0); } ``` ### 驗證 #### isEmpty * 有三種作法 1. 我的寫法,直接看input是不是為空字串 2. 判斷typeof是否為string, 是的話長度是否等於0 3. 直接判斷長度是否等於0,若沒有長度的話會是undefined * 驗證不能只考慮readline輸入為字串的情況 * 有關驗證的問題,總而言之就是,因為是「這個型別」,所以要對「這個型別」的輸入做驗證,所以驗證基本上就是跟型別綁在一起 * 驗證會依照它的名字去假定傳進來的是字串還是數字,例如isIntegerNumber就假定傳進來的是數字,所以轉型別的事情不是給驗證處理,而是放在驗證外 * 養成注意型別的習慣 * 拋出的錯誤,其型別應固定為Error object ```javascript export function isEmpty(input) { if (input === "") { throw new Error("請輸入內容"); } } ``` #### isEnglish * 魔法數字:64是什麼?91是什麼?要宣告成65=A, 90=Z ```javascript export function isEnglish(input) { for (let i = 0; i < input.length; i++) { // 舊的 if (input.charCodeAt(i) > 64 && input.charCodeAt(i) < 91) // 改成 const A = 65; const Z = 90; const a = 97; const z = 122; const space = 32; if (A <= input.charCodeAt(i) && input.charCodeAt(i) <= Z) { continue; } else if (a <= input.charCodeAt(i) && input.charCodeAt(i) <= z) { continue; } else if (input.charCodeAt(i) === space) { continue; } else { throw new Error("Please enter something in English"); } } } ``` #### isSpaceInFirstLast * 可以改用`"".trim`處理完再判斷長度是否跟原本一樣 * `"".trim`等同於String.prototype.trim(),用""代表String更為簡潔 * 注意不能改到原本的string ```javascript // 原本的寫法 export function isSpaceInFirstLast(input) { if (input.at(0) === " " || input.at(-1) === " ") { throw new Error("Please do not enter space in the first and last words"); } } // 可以改為 export function isSpaceInFirstLast(input) { const trimmedInput = input.trim(); if (trimmedInput.length !== input.length) { throw new Error("Please do not enter space in the first and last words"); } } ``` ### 其他問題 1. 直接return 用array method處理的陣列?還是先宣告一個變數,再return這個變數 - 要看這些method直不直覺 - 排版上要讓一行code只做一件事 - code讀起來像英文一樣就是好code - 要去習慣把code縮小,而不是多到超出視野,這也是為什麼Chris一開始不推薦大家使用雙螢幕 ```javascript // 原本寫的 function oddLettersOfName(name) { const oddLetters = Array.from(name) .filter((element, index) => index % 2 === 0); // 這個地方就比較適合用宣告變數,讓人家一看就知道這是要拿到奇數的字母 return oddLetters.join(", "); // 可以改成 // 直接return用array method處理的陣列 function oddLettersOfName(name) { // 把filter內的函式獨立出來,命名,賦予涵義 // 這個function擺在用到它的附近,在視線範圍內就可以 function oddLetters(element, index) { return index % 2 === 0; } // 一行code只做一件事 return Array.from(name) .filter(oddLetters) .join(", "); } ``` 2. 被說用到新的array method,node怎樣的版本算是新? - 註記專案使用的版本就好,不用記哪些method算是新的 - 這是系統性問題,不是語法問題 3. array method沒用到element,我看小銘用_代替,是公認的作法嗎? - 不是公認的作法,不過算是一種流派 - Chris會寫某個字母,例如o或x當做圈圈叉叉,代表一個記號 - 就像當code沒有很長的時候index可以用i代表一樣 4. for迴圈很少用到了嗎? - 幾乎不寫了 - for迴圈是知道長度的時候使用的 - 為什麼會知道長度?因為是array - 因為是array,所以可以使用array method - 可以參考Chris的[鐵人文章](https://ithelp.ithome.com.tw/articles/10209105) 5. isPrime 小於2的數是錯誤還是false - 看數字小於2是否為錯誤流程 - 照理說只要輸入數字就應該可以跑,所以不該丟錯誤