JavaScript === 變數 variable === ### 命名法 1. camelCase(myHeroName) 2. snake_case(my_hero_name) ## Hoisting 變數提升(二階段運作) 宣告變數 或 函式宣告都會產生hoisting,只是兩者有些微的不同;而宣告變數區分成var || const/let也有所不同。 ```JavaScript= b(); console.log(a); var a = 5 ; function b() { console.log("Hello") } // Hello // undefined ``` #### 宣告變項: 1. var a =1 console.log(a) * 建立期 a.找名字 b.初始化(給初始值) * 執行期 a.執行函數 b.賦值 2. let b =1 console.log(b) * 建立期 a.找名字 ~~b.初始化~~ TDZ(Temporal Dead Zone暫時死區) 將變數蓋起來 * 執行期 a.執行函數(將變數的蓋子拿開) b.給值 ==let是在執行時賦值== #### 函式宣告: 函式建立時,JS會把函式宣告移到作用域頂部,並且可以直接使用。 龍哥:函式在第一階段初始化+給值一起完成。 所以: ```JavaScript= b(); function b(){ console.log("Hello") } // 即便開頭先呼叫b函式,結果一樣是Hello ``` ## 流程控制 判斷式可以搭配強制轉型(coercion)的特性;用Boolean()判斷true or false。但要特別留意的地方是 0 也會被強制轉換成false,所以帶入的引數要確定不是 0,不然一樣無法順利執行。 補充:『undefined』 『null』 『+-0』 『''』經過Boolean皆為flase ## if...else... ```javascript if("判斷式"){ console.log( ) }if else( ){ console.log( ) }else( ){ console.log( ) } ``` ## switch case ```javascript switch( ){ case 1 : console.log( ) break case 2 : console.log( ) break default : console.log( ) break } ``` ### NaN #### NaN=Not A Number 如何判斷某個值是NaN let a = NaN ---JS內建語法-- if(Number.isNaN(a)){ console.log("它是NaN") }else{ console.log("它不是NaN") } // 原isNaN(a)會強制轉換成數字,以Number.isNaN代替 // 如果瀏覽器不支援的情況下,使用--魔法用法-- ---魔法用法--- if(a !== a){ ==console.log("它是NaN") }else{ console.log("它不是NaN") } ## 布林值Boolen (只有 true or false) #### Js中的 Falsey Value (只會被當false) - 0 ; -0 - '' ; "" - NaN - undefined - null - false ## 型別轉換 #### 轉換成‘數字’ - parseInt( ) - Number( ) / +( ) #### 轉換成‘文字’ - toString( ) ### == vs === : 兩者都會檢查型別 * console.log(123 == "123"); //ture 鬆散,且會做型別轉換 * console.log(123 === "123"); //false 嚴格 ==使用時機-> 皆以三個等號為主;如你知曉兩個值在雙等號時是相同的,則可使用雙等號== >For Example: ``` let a = undefined ``` >**// 已知undefined 和 null 都是空值,所以可以使用 ==** ``` if(a ==null){ console.log("沒有值") } ``` ``` if(a ===undefined || a ===null){ console.log("沒有值") } ``` ``` let age = 18 if(age==18){ console.log("18歲") }else{ console.log("不是18歲") } ``` ==一個等號是'指定',雙等號才是等於== ### ||-> 或者or ### &&->而且and 邏輯短路 x()=10秒 || y()=30秒 如x成立,則y有無成立都output x()=10秒 && y()=30秒 除了x成立外,y也得成立才output --- 可以用來代替if...else...(但新手通常都把|| &&放在判斷式中) - 當data為空值時 let data = null if(!data){ console.log("沒有data") } data || console.log("沒有data") //如data有值則印出data值,反之則印出console.log - 當data有值時 let data = 12345 if(data){ console.log(data) } data && console.log(data) //印出12345 ### 課堂實作:請輸入年齡 ```JS 1 const age = prompt ("How old are you?") 2 console.log ("You are ${age} year-old") // 這邊會出現錯誤的地方 // a. 沒寫,取消->null // b. 有寫,空白->'' // c. 亂寫 ->1~150 ``` =更完整寫法= ```Js 1 const age = prompt("How old are you?") 2 const ageNumber = parseInt(age) 3 if(ageNumber > 0 && ageNumber <= 150){ 4 console.log("You are ${ageNumber} year-old") 5 }else{ 7 console.log("NG") 8 } ``` ### REPL=Read-Eval-Print-Loop 讀你的指令-評估後-印出來-再讓你繼續下指令 like:直接在瀏覽器中打1+2 會主動印出3,即便你沒有打console.log ### 三元運算子 ( ) ? XXX : YYY - if...else...寫法 ```javascript let age=20 if(age>=18){ console.log("isAdult") } else { console.log("notAdult") } ``` - 三元運算子寫法 ```javascript let age=20 age>=18 ? console.log("isAdult"):console.log("notAdult") // 如果age大於等於18的話,output"isAdult"否則output"notAdult" ``` 迴圈 (for / while) === ### for 迴圈(起始狀態;結束條件;每回合做的事) ```javascript= for(let i = 0; i > 10; i++){ //迴圈中要做的事 } ``` ### while 迴圈 ```javascript= let i = 0 //預設值 while( i < 10 ){ //做到哪 //迴圈中要做的事 i = i + 1 //增加多少到下一局 } ``` ### continue 1. 跳過這回合不執行後面 ```javascript for(let i=0 ;i<10 ;i++){ if(i==5){ continue } console.log(i) } // 當遇到i=5的回合時跳過不執行,所以output:0.1.2.3.4.6.7.8.9 ``` 2. 放最後沒意義 ```javascript for(let i=0; i<10; i++){ console.log(i) if(i % 2 == 1){ continue } //因為他是用來跳過這個執行,結果你又不寫東西給他,很沒意義 ``` ### break 1. 舉例一 ```javascript for(let i=0; i<10; i++){ if(i==5){ break } console.log(i) } // 遇到5的part停止 output:0.1.2.3.4 ``` 2. 舉例二 ```javascript for(let i=0; i<10; i++){ console.log(i) if(i==5){ break } } // 印完5的part後面都停止不做 output:0.1.2.3.4.5 ``` ### 總結 ``` 1. continue視為pass這回合 2. break視為終止執行 3. 兩者都需要在迴圈中使用 ``` 函數 function === ### 一般寫法 ```javascript function hi(a){ console.log(a) } hi(a) ``` ### 匿名函數 anonymous function ```javascript const hi = function(){ console.log(123) } hi(a) ``` ### 箭頭函數 arrow function ```javascript const hi = ()=>{ console.log(123) } ``` 備註 ``` 以一般寫法來看: 在function中定義的a稱為『參數』 console.log(a)中的a稱為『變數』 而hi(a)中的a稱為『引數』 ``` ### function會遇到的情況 情況一 ```javascript let a =123 function hi(){ let a = 456 } hi() console.log(a) // 123 // 因為let a=456 只會在block中執行,並不會影響到function外的宣告 ``` 情況二 ```javascript let a=123 function hi(){ a=666 } hi() console.log(a) // 666 // 切記!不能亂指定! // 此情況在function中的a=666執行後,污染到了function外的宣告,因此output:666 ``` 情況三 (let不能重複宣告,var可以) ```javascript function hi(a){ //函式已經有a了 let a=456 //又重複宣告a console.log(a) } hi(123) //因為在function中重複宣告了a 所以出現以下錯誤 //Identifier 'a' has already been declared ``` 以情況三來看,如果改成 ```javascript let a=123 function hi(a){ a=456 console.log(a) } hi(123) console.log(a) // 456 // 123 ``` function中參數為a,所以在行為內的a=456是指,我把行爲中的a都指定為456去計算,所以並不會影響function外的a ### 回傳值 retrun value > 回傳值:完成函數後所得到的結果 1. 舉例一 ```javascript function hi(){ console.log(123) //console.log本來就會印出123 //return undefined,沒有寫回傳值(代表此函數沒結果) } console.log(hi()) // 123 //undefined ``` 2. 舉例二 ```javascript function hi(){ return console.log(123) //印出123是console.log本來就會做的事 //undefined是console.log函數沒有回傳值 } console.log(hi()) //123 //undefined ``` 3. 舉例三 ```javascript //寫法1 function isAdult(age){ if(age>=18){ return "ture" }else{ return "false" } } console.log(isAdult(20)) //ture //寫法2 function isAdult(age){ return (age>=18) //使用到布林值ture,false } console.log(isAdult(20)) //ture ``` ### Scope範圍(要去哪找?) > const,let = bolck scope 活不出{ } > var = function scope 活不出函數 1. 舉例一 ```javascript for(let i=0; i<10; i++){ var a=456 //可執行,block無法關注var } console.log(a) //456 for(let i=0; i<10; i++){ let a=456 //let活不出{ } } console.log(a) //a is not defined ``` 2. 舉例二 ```javascript //flag通常是用來表示一個布林型的值 function run(flag){ if(flag){ let message = "yes" //let活不出block }else{ let message = "no" //let活不出block } //上面皆可略過 console.log(message) //沒有message給他印出 } run(true) //Message is not defined at run function run(flag) { let message = "No" if (flag) { let message = "YES"; //活不出block } //直接跳上去看 let message = "No" console.log(message); //有東西給他印了 } run(true); //No function run(flag) { var message = "No" //var活不出function if (flag) { let message = "YES"; //let活不出block } } console.log(message); //沒有message給他去印 run(true); //message is not defined function hi(flag){ var uu=456 function ha(){ var message=123 //var活不出function } } hi() console.log(message); //沒有message給他去印 //message is not defined ``` ### 結合 變數提升概念(往前看note) **Function Statements隨時呼叫都可以; Function Expressions不行。如下:** ```javascript abc() var abc=function(){ console.log(123); } //因為變數提升 var回傳undefined //undefined() 不是函式 //TypeError: abc is not a function abc() let abc=function(){ console.log(123); } //abc is not defined //有變數提升 TDZ //未初始化 abc() const abc=function(){ console.log(123); } // Cannot access 'abc' before initializatio ``` ### 全域變數 window > 絕對〖 不要 〗不宣告,會污染全域變數 ### 嚴格模式 strict mode > 以字串的形式出現。(因為有些舊系統可能不支援) 陣列Array === ```javascript= let a = ["a","b","c"] 如要抓取"a"值-> console.log(a[0]) // 0->index索引值(偏移值) *參考goodnote筆記page5的圖* ``` ## 陣列取值 **動態寫法,chars陣列可能會長大** ```javascript let chars = ["a","b","c","d"] -> console.log(chars[chars.length-1]) ``` ## function是“物件” 函數等同變數,只是可被呼叫 > 高級函數(Higher-Order Function,HOF)中的函數可以是匿名函數 > 再想一次函數寫法: > 1. 一般寫法 function 函數名(參數){return ...} > 2. 匿名函數 const 名字 = function(參數){return...} > 3. 箭頭函數 const 名字 = (參數)=>{return...} ### .forEach(function( )) > 1. 沒有回傳值 > 2. 只幫忙做事,不收結果 舉例 ```javascript= const nums = [1, 2, 3, 4, 5]; // output:[1, 3, 5] const num = [ ] nums.forEach((n)=>{ if(n % 2 !== 0) num.push(n) //.forEach()要自己丟進去 }) console.log(num) ``` ### .push(元素1, 元素2..., 元素n) > 1. 從陣列後面塞進新元素 舉例 ```javascript= const nums = [1, 2, 3, 4, 5]; // output:[1, 3, 5] const num = [ ] nums.forEach((n)=>{ if(n % 2 !== 0) num.push(n) //.forEach()要自己丟進去 }) console.log(num) ``` ## .map(function(原陣列的元素, 索引值, 呼叫map方法的陣列 )) > 1. 收集成新陣列 > 2. 丟進去幾個,就會回傳幾個 > 3. return 舉例 ```javascript= const numbers = [1, 2, 3, 4, 5] // 每個元素乘以2 const double = numbers.map((n)=>{ return n * 2 //.map()會直接收集並且回傳 }) console.log(double) // [2, 4, 6, 8, 10] ``` ## .filter(function( )) > 1. 過濾出要的東西,並組成一個新陣列 > 2. 只做過濾,不改變值 舉例 ```javascript= const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 找出奇數 const odd = numbers.filter((n) => { return n % 2 !== 0 //如果是ture就留下那個數回傳 }) console.log(odd) // [1, 3, 5, 7, 9] ``` ## .reduce(function(acc, cv) => { } ,0) > 1. 做規劃、整理 > 2. acc = 累加值 ; cv = 目前值 > 舉例 ```javascript= const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] //加總 const sum = numbers.reduce((acc , cv)=>{ return acc + cv //第一次的acc+cv會是第二次的acc },0) //0為初始值,如不寫則少一跑一圈,但結果不會有變化 console.log(sum) ``` ## [.sort( a, b )](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 對陣列進行排序(由小到大) > 1. 對陣列進行排列(只會有原陣列中的元素) > 2. 會帶入compareFn比較回傳值 ```javascript= arr.sort((a, b) => { return a - b; //sort是一個比較函數 }) // a-b>0 ; a大於b -> [b, a] // a-b<0 ; a小於b -> [a, b] // a-b=0 ; a,b相等 ``` > 3. 如沒有帶入compareFn,則將陣列內的元素轉為字串,並以Unicode進行比較 > 4. undefined會排在非undefined元素前面 ## .join( ) > 1. 將所有元素轉為**字串** > 2. 回傳最終的字串,如arr.length為0,則傳回空字串 > 3. ( )內可以放『 , 』可將最終回傳的字串分開 四種不同的方式連接集群: ```javascript= const a = ["Wind", "Water", "Fire"]; a.join(); //'Wind,Water,Fire' a.join(", "); //'Wind, Water, Fire' a.join(" + "); //'Wind + Water + Fire' a.join(""); //'WindWaterFire' ``` ## .indexOf( 要找的元素, 從第幾個索引值開始找 ) > 只會回傳第一個找到的位置,如沒有找到變回傳-1 ```javascript= const arr = [1, 2, 3, 4, 5, 3, 2] const result = arr.indexOf(3) console.log(result) // 2 ``` ## .lastIndexOf( 要找的元素, 從第幾個索引值開始找) > 只會回傳要找的元素最後的位置,如沒有找到變回傳-1 ```javascript= const arr = [1, 3, 4, 5, 3, 4, 3] const result = arr.lastIndexOf(3) console.log(result) //6 ``` ## .slice( 開始擷取的索引值, 結束的索引值) > 擷取陣列中,[start索引值]到[end索引值]之前(不包括end的值)的元素 例: ```javascript= var str = "Hello, world!"; var slicedStr = str.slice(0, 5); //擷取從索引 0 到索引 5(不包括索引 5)的子字串 console.log(slicedStr); // 印出 "Hello" ``` DOM Document Object Model (文件物件模擬) === 例: HTML中 ```htmlembedded= <div id="doll-1" class="doll">Doll 1</div> <div d="doll-2" class="doll">Doll 2</div> ``` ## JS [DOM](https://ithelp.ithome.com.tw/articles/10202689) 拿HTML的東西 > JS沒辦法拿到HTML的東西,而是透過Runtime(俗稱瀏覽器)作為媒介取得。瀏覽器會將HTML的標籤結構,解析且變成物件(DOM tree)後給JS使用。 1. document.getElementById(" ") ```javascript= const d1 = document.getElementById("doll-1") console.log(d1) //<div id="doll-1" class="doll">Doll 1</div> ``` 2. document..querySelector("# ") >- 即便HTML文件中有一樣名稱的元素,也**只會抓一個**(由上往下),且**抓第一個出現的** >- 可以運用CSS的選取器的方式來選,例如,"h1:nth-child(3)" 選取第三個h1 ```javascript= const d2 = document.querySelector("#doll-1") console.log(d2) //<div id="doll-1" class="doll">Doll 1</div> ``` 3. 一口氣抓兩個娃娃 ```javascript= const d3 = document.getElemensByClassName("doll") console.log(d3) //HTMLCollection //<div id="doll-1" class="doll">Doll 1</div> //<div d="doll-2" class="doll">Doll 2</div> ``` 這邊會抓出一個元素集合(不是陣列,是物件)。 集合中的每一個都是一個元素。 會動態更新集合裡的元素。 4. ```javascript= const d3 = document.querySelectorAll(".doll") console.log(d3) //NodeList //<div id="doll-1" class="doll">Doll 1</div> //<div d="doll-2" class="doll">Doll 2</div> ``` 括號中要使用CSS選取器用法。 這邊的集合中每一個都是一個節點(不是陣列,是物件)。 總結:HTMLCollection V.S. NodeList 都不是陣列,兩者差別在於能執行的行為不同。 ## 監聽 .addEventListener( ___,( ) =>{ } ) ### DOMContentLoaded 先監聽完整份文件後,再進行其他事,才不會輸出null 舉例 ```javascript= document.addEventListener(DOMContentLoaded,()=>{ comst el = document.querySelector("h1") consolr.log(el) }) //先讀取完整份文件後再執行querySelector的行為 ``` 或者,你也可以 < script **defer** src="app.js">< /script> 在這邊加上**defer**(意為延遲解析) ### defer 、 async 、 type=“module” ```javascript= <script src=“”my.js” type=“module”> //same as defer ``` type=“module” defer 則一使用 ```javascript= <script src=“”my.js” type=“module” async> ``` type=“module” async 可以搭配使用 ### type=“module” > 情境一 ```javascript= <script src=“a.js”> <script src=“a.js”> // 載入兩次a.js ``` 情境二 ```javascript= <script src=“b.js” type=“module”> <script src=“b.js” type=“module”> // 只會載入一次b.js cuz type=“module”會判別檔名,不會重複執行 ``` 情境三 ```javascript= <script src=“c.js.1” type=“module”> <script src=“c.js.2” type=“module”> // 兩個都會載入 ``` ### 預設行為 例如:超連結 < a href="">...< /a>,按了後跳出網頁為其的預設行為。 擋掉預設行為作法: ```javascript= const link = document.querySelector("a") //呼叫超連結物件 link.addEventListener("click", (e)=>{ e.preventDefault //擋掉超連結的預設行為 console.log(go) }) ``` ### Set(集合) > Set(集合)中的元素都是獨一無二,不會重複 ES6語法 === ### 物件簡寫(key與value一樣時) ```javascript= const name = "cc" conast age = "18" const hero = { name: name age: age //如果key與value一樣可以簡寫 } 簡寫成 -> const hero = { name, age } ``` ## 解構 ( 重要!)可以把物件簡化,也能一個一個拆開看 情況一:物件的解構 ```javascript= const hero = { name: "kk", age: 18, power: 100 } const name = hero.name; const age = hero.age; //解構 -> const { name, age } = hero //大括號內的順序不重要 ``` 情況二:陣列的解構 ```javascript= const nums = [1, 3, 5, 7] const first = nums[0] const last = nums[nums.length - 1 ] //解構 const [first, second, third, fourth] = nums console.log(first, fourth) //1,7 ``` 情況三:函數的解構 ```javascript= function bmi(user){ 原寫法->//const height = user.height; //const weight = user.weight; 解構後-> const { height, weight } = user return height * weight * 2; } const me = {height: 100, weight: 200} console.log(bmi(me)) ``` 也可在**參數**中直接解構,如下 ```javascript= function bmi({ height, weight }){ return height * weight * 2; } const me = {height: 100, weight: 200} console.log(bmi(me)) ``` # [...a, ...b] 展開或收集成陣列 陣列展開 ```javascript= const a = [1, 2, 3] const b = [4, 5, 6] //console.log(a.concat(b)) 合併兩陣列原方法 //展開 const c = [...a, ...b] console.log(c); ``` ```javascript= function hi(a, b, c){ console.log(a, b, c); } const data [1, 2, 3] hi(...data) // 1,2,3 ``` 收集陣列 ```javascript= const nums = [1, 2, 3, 4, 5] //收集 const c = [first,...second] console.log(c); //1 //[2,3,4,5] ``` 展開 v.s. 收集 ```javascript= function hi(a, b, c, ...others){ console.log(a, b, c); //1,2,3 console.log(others); //4,5 } const data [1, 2, 3, 4, 5]; //收集給console.log(others) hi(...data); //展開帶進console.log(a, b, c) ``` ### 多維陣列 -> 一維陣列 .flat( ) ```javascript= function log(...params) { if (params.length == 0) { console.log(""); return; } console.log(...params.flat()); //用flat攤平再用...陣列展開 } log(); log([1, 2, 3]) // 1, 2, 3 ``` # fetch( ) :拿回來 > 因為會回傳promise,所以需要使用then來解(但也有其他的方式)。 舉例: ```javascript= const userList = " 網址 "; const result = fetch(userList); console.log(result) // promise (保證會回來但不知道什麼時候回來) ``` 印出要的東西->用then解,如下 ```javascript= const userList = " 網址 "; //用then解 const result = fetch(userList).then(() => { console.log("ok") }) // ok ``` 網址: URL API = Application Programming Interface(存取網址的介面)= 應用程式介面 ```javascript= const userList = " 網址 "; //用then解 const result = fetch(userList).then((resp) => { return resp.json() }) // promise ``` .json( )=JavaScript Object Notation 物件表示法 >將text經過json轉換成能在JS中使用的物件(類似DOM tree作用) ```javascript= const userList = " 網址 "; const result = fetch(userList) .then((resp) => { return resp.json(); //放到resp裡做回傳resp.json()的動作 }) .then((data) => { //解構,原d.name data.forEach(({ name } => { console.log(name) }); }) // 輸出網址中的所有名字 ``` #### 拿東西 如果HTML的標籤:< div id="xyz" data-ccc="123" data-a"432">...< /div> const x = docuent.querySelector("#xyz") console.log(x.dataSet) # 遞迴 (費式數列) >費式: F(0)=0 F(1)=1 F(n)=F(n-1) + (n-2) ```javascript= function fib(n) { if(n < 2){ return n } return fib(n-1) + fib(n-2) } //在函式中呼叫自己會有堆疊液出的問題,這邊不會 //可以改用迴圈的方式寫看看 ``` # for in / for of **for in** 找key用的 舉例: ```javascript= const hero = { name:123, age: 456, power: 789 } for(let h in hero){ console.log(h) //namm, age, power console.log(hero[h]) //123, 456, 789 } ``` **for of** 找值(value) ==用於可迭代的對象,例如陣列、字符串、Map、Set...== 物件不可迭代,所以上面for in的舉例就不能使用for of來執行。 舉例: ```javascript= const arr = [1, 2, 3]; for (let elem of arr) { console.log(elem); // 输出 1, 2, 3 } //陣列可以迭代 const str = 'hello'; for (let char of str) { console.log(char); // 输出 'h', 'e', 'l', 'l', 'o' } //字串可以迭代 ``` # by value v.s by reference ### by value 在 JavaScript 中 primitive type(Boolean, String, Number, null, undefined)都屬於 By Value 例如: ```javascript= var a = 10 ; var b ; b = a ; a = 5 ; console.log(a) // 10 console.log(b) // 5 ``` ![image](https://hackmd.io/_uploads/HJAM2TkxA.png) 想像a和b分別存在不同的記憶體中,所以改變b的值時,不會影響到a,這樣的情況就是by value。 ### by reference 在 JavaScript 中 Objects(Object, Array, Function)都屬於 By Reference。 例如: ```javascript= var c = { greeting: 'Hello' }; var d; d = c; d.greeting = 'Hola'; console.log(c) //'Hola' console.log(d) //'Hola' ``` ![image](https://hackmd.io/_uploads/S1S1Ca1eC.png) by reference中,想像c和d存在同一個記憶體中,所以不管是改變c或d的內容,兩者都會一起改變。 # jQuary > JQuary是JavsScript延伸出來的套件 ## localStorage # shallow copy : 淺層複製 > 如果遇到多維陣列,只會複製到第一層陣列 # module 模組系統 ## inport ## export ## [setTimeout](https://developer.mozilla.org/zh-CN/docs/Web/API/setTimeout)(未執行的function, 要延遲多久時間) > 第一個參數=> callback function 0325 JavaScript Note # Regular Expression (REGEX) 常規表示法 [REGEX符號運用](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Regular_expressions) > '.' => 任何字 > '+' => 1個以上 > '?' => 0個 || 1個 > 可以用反斜線區隔同樣符號不同功能 eq: 判斷是否為email //xx@gmil.com REGEX: .+@.+\..+ 翻譯成中文 =>(很多個任何字@很多個任何字.很多個任何字) ```JavaScript= function isValidEmail(mail) { const pattern = / .+@.+\..+ / if (mail.match(pattern)){ return true; } return false; } const m = "xxg@gmil.com.tw"; console.log(isValidEmail(m)) ``` ```JavaScript= function isValidEmail(mail) { const pattern = /.+@.+\..+/ //記得用/ /把正規表示法包起來,不能留空!!! //return Boolean(mail.match(pattern)) return !!mail.match(pattern) //可以用‘兩個驚嘆號’代替Boolean(),來轉換回傳的值(要回傳布林值) } const m = "xxg@gmil.com.tw"; console.log(isValidEmail(m)) ``` # this > 通用 > 跟寫在哪無關,只跟怎麼被呼叫有關 ### this規則 #### 1. 誰呼叫誰就是this , 無人呼叫this=global 全域物件(window) 呼叫的前面有人: ```JavaScript= const hero = { name:"kk" attack: function(){ console.log(this); }, }; hero.attack() //前面有hero,所以this=hero ``` 呼叫的前面沒人: ```JavaScript= function cc(){ console.log(this) } cc() //印出windows,因為cc()前面沒人 ``` 兩層function時 ```JavaScript= function cc() { function aa() { console.log(this) //印出window } aa(); } cc(); ``` #### 2. 確認是否有使用箭頭函數(箭頭函數沒有自己的this,所以會印出window) 箭頭函數沒有argument: ```JavaScript= const hi = () => { console.log(argument) //argument是ES6前用來印出不固定數的引數的方法,現在都用...(展開陣列) } hi(1, 2, 3, 4, 5) //output壞掉,因為箭頭函數沒有自己的argument ``` 箭頭函數沒有this: ```JavaScript= const hi = () => { console.log(this) } hi(1, 2, 3, 4, 5) //output 全域物件,因為箭頭函數沒有自己的this,this會往外成找 ``` 比較一般函數 及 箭頭函數 使用this時: ```JavaScript= const hero = { name: "kk", a: () => { console.log(this); }, b: function() { console.log(this); }, }; hero.a() // 印出window hero.b() // 印出hero ``` #### 3. 是否有使用 new (有 new 走物件導向的流程) // {} 舉例: ```JavaScript= function a() { console.log(this); } new a() //a{} ``` #### 4. 是否有用call || apply (兩者用法皆是把this轉向),bind(先把h帶入成this,產出新function) call || apply舉例: ```JavaScript= function a(){ console.log(this.name); //要印出 kk } const h = { name: "kk" }; a.call(h); a.apply(h) //兩種方式都能把h帶入 function 當作 this (注意!!不是把h當參數,而是把 h 當作 this!!) ``` 如果,也想把引數帶進參數的話: ```JavaScript= function a(x, y){ consolr.log(this.name) } const h = { name: "kk" } const data = [1, 2] //apply用法 a.call(h, 1, 2) //引數一個一個帶進去 ca.apply(h, data) //引數一整包 data 帶進去 ``` call || apply 使用情況: ```JavaScript= const hero = { hp: 100, mp: 30, attack: function() { console.log("ATTACK!!"); } }; const mage = { hp: 60, mp: 100, attack: function() { console.log("attack~~~"); }, heal: function() { this.hp+ = 30 } } mage.heal() //這裡的法師只能幫自己補血 console.log(hero.hp); //印出100 mage.heal.apply(hero); //用 apply 把 hero 帶入成 this,讓法師也能幫英雄補血 console.log(hero.hp) //印出130,因為已經補完血了 ``` bind(一定要呼叫,才會有結果)舉例; ```JAvaScript= function a() { consolr.log(this); } const h = { name: "kk" } const f = a.bind(h); //把 h 帶入成 this,然後 產出一個新的 function f(); //啟動由 bind 產出的新 function,如果想帶引數進去,從這邊帶 conaole.log(f) // ``` bind 使用時機: ```Javascript= function ass(a, b) { return a + b } //partial function(局部函數) const add5 = add.bind(null, 5); //這邊的null是帶入成this,所以跟函數內要回傳的算式沒關係。這邊b都還沒有被帶入任何引數。 console.log(add5(10)); //15,這邊把10帶入給b console.log(add5(3)); //8,這邊把3帶入給b console.log(add5(8)); //13,這邊把8帶入給b console.log(add5(100)); //105,這邊把100帶入給b ``` #### 5. 是否有開啟 strict mode (嚴格模式) ```JavaScript= "use strict" function a() { console.log(this) } ``` > 總結this的五點規則: > 1. 是否有開起 strict mode > 2. 是否有使用 new > 3. 是否有使用箭頭函數 > 4. 是否有用 call , apply , bind > 5. 誰呼叫,誰就是 this ; 無人呼叫 this = global 全域物件 # 閉包 clousure 閉包只是一種手法,因稍後延遲發生某事,若不先存取就無法帶入當下的資料。不等於 閉包是逃避的做法。 ```JavaScript allBtn.forEach((btn) => { btn.addEventListener("click", function() { var that = this //閉包,把this變成that帶走做事(逃避的作法) const handler = function() { console.log(that) }; }) }) ``` 比較使用bind: ```JavaScript allBtn.forEach((btn) => { btn.addEventListener("click", function() { const handler = function() { console.log(this); }; const nowHandler = handler.bind(this); //bind 會先把 this 收著, newHandler(); //這邊是在呼叫 bind 生成的新 function, setTimeout(newHandler, 1000); //在1秒後執行。 }) }) ``` 以上兩個方法都有閉包的發生,只是 var that=tihs 是逃避的做法,而使用 bind 是對 this 掌握度較好 比較使用var(全域污染) 及 let(閉包): ```JavaScript= for(var i = 0; i < 3; i++){ setTimeout(()=> console.log(i)) } // 印出 3 3 3 // 因為 var 會污染全域變數,所以在此函數中 setTimeout 執行完從 Q 要回 stack 時,i 已經變成3了 for(let i = 0; i < 3; i++){ setTimeout(()=> console.log(i)) } // 印出 0 1 2 // let 只能存在 block scope,因而產生閉包。等 setTimeout 完成回到 stack 後 i 就找不到了,所以要帶著 i。 ``` # IIFE (Immediately Invoke Function):定義完馬上執行的function > 在IIFE裡的東西不會流到外面去 => function scope > jQuery就是一個IIFE ```JavaScript= for(var i = 0; i < 3; i++) { setTimeout(((y) => { console.log(y); })(i), 0); } ``` 0408 JS Note # lexical scope || dynamic scope(動態執行) - scope : 在哪找東西 - lexical:跟寫在哪有關,跟怎麼執行無關 - dynamic:跟怎麼執行有關 like : this 舉例: ```JavaScript let abc = 456 function a(){ let abc = 123 console.log(abc); } // 123 ``` # pass by value || pass by reference 要把東西傳進去才會有這兩種差別產生 舉例: (要把o還是c傳入這個hi的function裡) ```JavaScript function hi(data){ } const o = {name: 'cc'} ; const c = 123 ; hi(c) //? 傳常數進去,本來就不能改,所以-> by value hi(o) ``` //! 比較 data = 123 和 data['xx'] = "aa" 差別 # AJAX 非同步: JS在瀏覽器是單執行序(環境給他的),一次只能執行一件事,透過非同步,讓執行效能增加。 like : setTimeOut(), fetch()... # tagged function //FIXME 思考這題 : ```JavaScript function getPersonInfo(str, ...params) { const result = str.map((s, i) => { //s->str的元素,i是索引值,把i給params用 return [s, params[i]]; }); console.log(result.flat().join("")); } ``` 今日課堂內容: # target v.s currentTarget # try / catch # 測試 測試 = 自動話測試 = 寫一段程式碼,測試某功能是否正確運作 like: ```JavaScript function add (a, b) { return a + b ; } if (add(1, 2) == 3) { console.log("OK"); } else { console.log("ng") } ``` ## TDD(Test Driven Development)測試驅動開發 ```JavaScript //先寫 if ... else ... //? 規格、測試(一開始一定會壞,因為function還沒寫) if (add(1, 2) == 3) { console.log("OK"); } else { console.log("ng") } //? 實作 function () {...} ``` ## 測試套件 jest ```JavaScript it("測試的描述", () => { expect(true).toBe(true); //要做的事 }); ```