# CH5 JS Basics javascript最初創建的目的是為了**讓網頁活起來**,這種語言的程序稱為腳本(script)。javascript可以直接被寫在html,並在頁面加載時自動運行。 Vanilla JavaScript: 純javascript,而不需要任何而外的library(eg. jQuery)或框架。 每個瀏覽器有自己的js引擎,用來讀取並編譯js程式碼,若要確認某個瀏覽器的js引擎是否有支援某種功能,可參考 https://caniuse.com ## `<script>`放哪裡? 通常放在html的最下方,與CSS`<link>`不同,因為將js放在HTML正文的底部時,他會在任何js之前讓html, CSS有時間加載。`<script>`被放在HTML最下方所以是最後才會被加載。 先讓瀏覽器加載HTML, CSS,用戶不用等JS被解析完成,即可在網頁中看到某些內容,比較容易將使用者留下。 ## 常見javascript函數 `console.log()` -- 將message輸出到控制台 `window.alert()` -- 提醒框 `window.prompt()` -- 有輸入格的提醒框 ```javascript let user_name=window.prompt("type your name"); window.alert("hihi" + user_name); ``` ## Lexical Structure 自然語言(Natural Language): 1. 語法/文法grammar 2. vocab 程式語言(Programming Language) 1. 語法syntax 2. 保留字reserved word Lexical Structure是一組基本規則(syntax)。 1. Case Sensitive: js的大小寫有差別 2. 空白、換行會被忽略: minification(刪除空白鍵、換行鍵) 3. 註解: ```// 單行註解```、`/*多行註解*/` 4. 變數名稱需要由文字、underscore`_`、dollar sign`$`當開頭 5. js內部有關鍵字(reserved word, keyword)不能當變數名稱 6. js使用Unicode字元集合,所以string內部可由任何Unicode文字組成 7. Semicolons`;`用來分隔程式語句,在js中可用可不用 ## 變數與賦值 * 變數varible * 賦值assignment PS. 語法糖syntax sugar支援x=x+1更改為x+=1 ### 宣告變數declare varible 1. 若數值的值會變動,使用`let`宣告 2. 若數值不會驗動,則使用`const`(constants常數)來宣告變數 3. 不要使用var宣告變數 需注意的規則: 1. 用const宣告變數,一定要馬上給予初始值(initializer)。若用let宣告變數但還沒賦值,則變數的值是`undefined` 2. 用const, let宣告的變數不能重複宣告(redeclaration) 3. const不能重複賦值(reassignment) PS.JS引擎中有一個稱為garbage collector的後台程式,來監視所有物件並刪除那些變得無法訪問的object ## 數據(資料)類型data type ==7種基本數據類型(primitive data type)== 1. Number -- 整數與帶小數點的數字 2. BigInt -- 任意長度的整數 3. String -- 字符串 4. Boolean -- true, false 5. null -- 用來代表某個故意不存在的值 6. undefined -- 未被賦值的變數 7. symbol -- unique identifier PS.第八種數據類型為object,屬於non-primitive data type,可能為array, object, function... ### Number 允許精確地表示介於2^(-253)和2^(253)之間的所有整數。如果使用大於此的整數值,可能會丟失數字的精度。 > JS處理有小數值的方式是採用double-precision 64bit binary format IEEE754 encoding 支援的運算符號: `+`,`-`,`*`,`/`,remainder(餘數) operator`%`(mod),exponentiation(指數) operator`**`,`++`,`--`,`+=`,`-=`,`/=`,`*=` [x++, ++x差異](https://dev.to/yubo0826/x-x-chai-bie-yu-qi-zhong-de-han-yi-3e21) ### String 1. 在JS中可使用單引號或雙引號。 2. 兩個string的串接(concatenation)由`+`所完成的。 3. 不能做`-`,`*`,`/`的運算,若嘗試此類運算,因JS還是會嘗試算出某個值的結果,但兩個operand的值都不是數字,無法算出一個數字結果,結果會是`NaN`(Not a Number)。 4. `\n`換行 js是弱型別語言,會在背地裡將該變數轉換成可以被執行的資料型態,強行去執行。 ```javascript 20+30+'string' //output: 50string 20+30+'string'+10+15 //output: 50string1015 ``` ### Number Methods * `toString()` -- return一個數字的string ```javascript //x is a number(x is an object) let x = 27; //對x來說,toString()是一個method or function console.log(x.toString()+27); //output: 2727 console.log(typeof x); //number ``` * `toFixd(n)` -- return被轉換的數字,到小數第n位數 ```javascript let pi = 3.141592653589; console.log(pi.toFixed(2)); //output: 3.14 console.log(typeof pi); //number ``` 二進制不能精確地表示所有小數,這可能會導致意外結果,eg. `0.1+0.2 === 0.3` return false #### String Attrubutes and Methods [string](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/String) * `length` -- return string長度(string的屬性) * `[n]` -- return index第n項的字(index從0開始計算),`str.length-1`為最後一個字的index * `slice(indexStart[, indexEnd])` -- 提取字串的一部分作為新的string返回,而不修改原始的字串,indexStart是inclusive(會被包含),indexEnd可不填,exclusive(不會被包含) * `indexOf(substring)` -- return substring的第一個index位置,若找不到則return -1 * `toUpperCase()` -- return轉換為大寫的string,不會影響到string本身 * `toLowerCase()` -- return轉換為小寫的string,不會影響到string本身 * `splite(pattern)` -- return 透過搜索將一個字串以pattern分成一個有序的array,pattern可以是regular expression(正規表達式) * `startsWith(s)` -- 確定string是否以指定字串s開頭,根據需要回傳true或false * `endsWith(s)` -- 與`startsWith(s)`同,但確認結尾 * `includes(str)` -- return true如果string內部包含str * `charCodeAt(n)` -- return int between 0 and 65535,表示給定index的UTF-16 code unit 常見編碼方式: ASCII ### Boolean true, false Unary(單一) operator`!`可以將boolean的值反轉 ### Undefined & Null #### undefined 1. 已宣告變數,卻沒有賦予initializer時,會出現undefined。 2. functions預設的return value為undefined。 #### Null 用來代表某個故意不存在的值 ## JavaScript operators 運算元(operand)、運算符(operator),eg. a+b, `a` `b` are operands, `+` is an operator * assignment operator * comparison operator * logical operator * typeof operator(unary) * negation operator `!` * increment operator `x++`, `x--` * bitwise operator * srithmetic operator `+`,`+=`,`-=`, ... ### Comparison Operators js的comparuison operators的運算元是兩個任意資料型態,運算結果為boolean值 * `==` -- return true if operands are equal * `!=` * `===` -- return true if operands are equal and of the same data type * `!==` * `>`, ... ### Logical Operators operands為任意資料型態,且運算結果為boolean ![image](https://hackmd.io/_uploads/HkSNT-xUa.png) ### Bitwise Operators 在decimal系統中,可用的數字集合是`{0,1,2,3,4,5,6,7,8,9}`,由於在9之後就沒有數字可用了,因此使用2位數來標記下一個數字。 在binary系統中,可用的數字集合是`{0,1}`,在1之後則需要進位來代表下一個數字 => 0,1,10,11,100,101,110,111,... |binary |0|1|10|11|100|101|110|111|1000| |-------|-|-|- |- | - | - | - | - | -- | |decimal|0|1|2 |3 | 4 | 5 | 6 | 7 | 8 | 若要把二進制的數字轉換成十進制,公式為: (數學歸納法) ![image](https://hackmd.io/_uploads/H1P-yflL6.png) [binary to decimal](https://www.rapidtables.com/convert/number/binary-to-decimal.html) js的bitwise(每兩個bit) operators將數字operand視為32bits的數字(binary),並且對每個bit進行運算。 bit -- binary digit ` let a = 10, b = 9;` * `a&b` -- 在兩個operand的對應位都是1的位置返回一個1 (AND) ```javascript console.log(a&b); // 8 a=1010 b=1001 > 1000 ``` * `a|b` -- 在兩個operand的對應位都是0的位置返回一個0 (OR) ```javascript console.log(a&b); // 11 a=1010 b=1001 > 1011 ``` * `a^b` -- 在兩個operand的對應位相同的每個位置返回0 (XOR) ```javascript console.log(a&b); // 3 a=1010 b=1001 > 0011 ``` * `~a` -- 反轉operand的每個bit ```javascript console.log(~a); // 5, js顯示-11是因為tools component?的問題(學CPU才會知道,可忽略) a=1010 > 0101 ``` * `a<<b` -- 將二進制表示中的a向**左**移動b位制,丟棄任何被移出的bit ```javascript console.log(a<<1); //20 a=1010 所有都向左移動一格,右邊補0 > 10100 ``` * `a>>b` -- 將二進制表示中的a向**右**移動b位制,丟棄任何被移出的bit ```javascript console.log(a>>1); //5 a=1010 所有都向右移動一格,最後的0丟棄 > 101 ``` #### 何時會用到Bitwise Operators? :::info * 編碼運算 * 資料傳出,sockets programming, ports * 資料加密、SHA函數 * 作業系統、CPU * Finite State Machine * Graphics(eg. 影像處理、人工智能) ::: ## if statement if是最簡單的決策語句 `if(condition)statement;` ```javascript if(condition 1){ statement 1; }esle if(condition 2){ statement 2; }esle{ statement 3; } ``` ### Tyuthy and Falsy Values 在javaScript中,每個值(data type)在boolean context之下,都可以被視為true或false。在boolean context之下,js會自動做type coercion,常見的boolean context: 1. if statement 2. logical operators運算 3. others js中的Falsy Values: * false * 0, -0, 0n(BitInt) * "", '', `` ("".length=0) * null * undefined * NaN 除此之外的都是Truthy Values,包含[] empty array, {} empty object...等 ## ==Coding Convention(習慣) and Restrictions== ### coding convention: 1. 變數與函數的名稱全部小寫,若由2個以上的單字組成,則使用camelCase或用underline [常見重點整理 - 命名慣例 & 開發時注意事項](https://hackmd.io/@Heidi-Liu/note-common) 2. operators前後加空白鍵 3. 用分號結束一個簡單的statement 4. Constants都使用大寫 5. Class由大寫字母開頭 ### coding restrictions: 1. 變數、函數名稱不可由數字開頭 2. 變數、函數名稱不可包含Hyphen`-` 3. 變數、函數名稱不可使用reserved words # CH6 JS Basics 2 ## function 函式(method 方法) ### 數學函數 函數應該只返回一個值 `f(x)=3x+6`,x為參數(parameter) `f(5)`,5為自變數(argument) ### javascript function [Functions](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Guide/Functions) declare a function ```javascript // declaration, definition function FuncName(para){ statements return result; } //function exection, Invoke a function, function invocation, call a function //調用function FuncName(); ``` 若無return語句,則return undefined(js的function默認的return value) ```javascript undefined + undefined //NaN ``` ```javascript function convertor(c){ return c * 1.8 + 32; } let input = Number(prompt("請輸入溫度(C)")); let result = convertor(input); alert("換算後的溫度為" + result + "F"); ``` 每個js function實際上都是一個object(有instance properties, instance methods) ## Array 陣列 array不是primitive data type,可儲存用途或性質相近的資料 `let arr = []` 1. js array可以調整大小,且可包含不同資料型態 `srr = [1, 'A']` 2. js array中的元素必須使用非負整數作為index 3. js array的第一個元素為0,最後一個為array.length-1 4. js array會複製reference ```javascript let friends = ['gracr', 'mike', 'spencer']; let anotherVar = friends; // 複製reference anotherVar[0] = 'michael'; console.log('friends arr: '); console.log(friends) // 'michael', 'mike', 'spencer' console.log('anotherVar arr: '); console.log(anotherVar); // 'michael', 'mike', 'spencer' ``` ### Array Instance Properties * `.length` ### 常見的Array Instance Methods * `push(element)` -- 將一個或多個元素添加到array的末尾,並return array的新長度 * `pop()` -- 從array中刪除最後一個元素並return該刪除的元素 * `shift()` -- 從array中刪除第一個元素並return該刪除的元素 * `unshift(element)` -- 將一個或多個元素添加到array的開頭,並return array的新長度 當array內部的元素還有array時,稱為array of arrays ```javascript let myArr = [['name', 'address', 'age'], ['mike', 'taiwan', 35], ['john', 'us', 26]]; console.log(myArr[1][0]); // mike ``` ## <補充>==primitive data type vs. reference data type== > RAM(Random-access memory, 記憶體) ### primitive data type copy by value ```javascript let deposit = 500; //在RAM找到一個位置存取deposit=500 let anotherDeposit = deposit; //找到另一個位置存取anotherDeposit anotherDeposit = 600; // anotherDeposit = 600 console.log('deposit: ' + deposit + ', anotherDeposit: '+anotherDeposit); // deposit: 500, anotherDeposit: 600 ``` ### reference data type copy by reference,for 減少記憶體占用, OOP(物件導向程式設計) ```javascript let friends = ['a', 'b', 'c']; // 將friends指向a, b, c的位置 let another = friends; // 將friensds的位置給another another[0] = 'd'; // 將'a'位置的值改成'd' ``` ### `==` 差異 ```javascript let x = 10; let y = 10; // x, y各指向不同的RAM位置且值=10 // 因為x, y是primitive data type,因此比較時js會比較指向RAM位置的值 console.log(x == y); // true ``` ```javascript let arr1 = [1, 2, 3]; let arr2 = [1, 2, 3]; // arr1, arr2分別指向RAM不同位置的[1, 2, 3] // 因為arr1, arr2是reference data type,因此比較時js會比較指向的RAM位置 console.log(arr1 == arr2); // false ``` ## Function的時間複雜度 在CS中演算法的時間複雜度(Time complexity)可以描述該演算法執行時間,常用`O`(Big O Notation)表示,不包括函式的低階項和首項係數。 * 橫軸(x)為參數大小,縱軸(y)為執行時間 * `f(n)=5n^3 + 3n` -> 不包括函式的低階項(`3n`)和首項係數(`5`) -> 時間複雜度=`O(n^3)` 為了計算時間複雜度,通常會估計演算法的操作單元數量,每個單元執行的時間都是相同的。 時間複雜度: 在參數逐漸變大的形況下,function執行完畢所需要的時間的成長速度。 ### Function的時間複雜度 js陣列常見Method的時間複雜度: * `push` -- O(1) (參數的成長和所需完成的時間無關) * `pop` -- O(1) * `shift` -- O(n) (把前面的元素去掉,後面所有的元素往前移一格) * `unshift` -- O(n) 每個js引擎內部對於陣列的實現方法略有差異,對於不同大小的陣列,可能會使用其他更好的資料結構,eg. double-linked list, binary search tree(BST)。 ### Big O Notation Formal Definition ![image](https://hackmd.io/_uploads/HJsG8qILa.png) ## Object 物件 1. 每個js的物件都有properties, method 2. 屬於物件的function被稱為method 3. 物件的屬性與相對應的值是一種key-value repair 4. 獲取物件的方式可以透過dot notation或`[]` 5. js object是一種hashtable雜湊表(O(1)) ```javascript let obj = { first_name: "Wilson", last_name: "Ren", age: 26, is_married: true, spouse: "Grace", sayHi() { console.log(this.first_name + " says Hi"); }, walk() { console.log(this.first_name + " is walking..."); }, speak(words) { console.log(this.first_name + " says " + words); }, }; console.log(obj.first_name); // dot notation console.log(obj["first_name"]); // [] obj.sayHi(); obj.speak("Happy"); ``` 6. `this`指的是調用該方法的物件,若某個function沒有調用該function的物件,則`this`是指向global物件(在瀏覽器中指的是window物件) ```javascript function hello() { console.log("hello"); console.log(this); } hello(); ``` 7. 在js中function, array都是object (special type of objects) ```javascript function hello() { console.log("hello"); } console.log(typeof hello); // function console.log(typeof [1, 2, 3]); // object -> bug ``` [Array.isArray()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray) ## Loop 迴圈 重複做某件事,`while loop`, `do while loop`, `for loop`,若把`return`放到loop內部,循環會馬上停止。 ### for loop 1. initialization: declare循環開始前的計數器變數 2. final expression: 每次循環迭代結束時要執行的程式碼,一般用於更新或遞增計數器變數 ```javascript // initialization: declare循環開始前的計數器變數 for(initialization; condition; final expression){ ... }; ``` ### while loop 若忘記加計數器而導致while loop無限循環,電腦CPU可能會癱瘓 * 適合不知道loop要執行幾次的時候 ```javascript while (condition){ ... }; ``` ### do while loop 該循環指定的statement,直到條件=false * 先執行語句後再評估條件 ```javascript do{ ... } while(); ``` ### Nested Loop 巢狀迴圈 在loop內部還有另一個loop的情況 ### break, continue * `break` -- 終止迴圈,如果在Nested Loop中使用,只會終止使用包含`break`的loop,若需要終止Nested loop,則需要使用`return` * `continue` -- 跳過`continue`之後的循環內code,強制執行循環的下一次迭代 ### loop跑array ```javascript let arr = ["Mike", "Grace", "Json"]; for(let i = 0; i < arr.length; i++){ console.log(arr[i] + " is my friend."); } ``` ## 練習 ### 終極密碼 ```javascript let ans, startNum, endNum, isFlag = true; function init() { // 0 <= Math.random < 1 // 0 <= Math.floor(Math.random * 100) < 1 ans = Math.floor(Math.random() * 100); startNum = 0; endNum = 99; } while (isFlag) { init(); while (true) { let yourNum = prompt("請輸入你的猜測(" + startNum.toString() + "~" + endNum.toString() + ")"); if (yourNum < startNum || yourNum > endNum) { alert("無效猜測,請重新猜測一個數字"); continue; } if (yourNum == ans) { isFlag = confirm("猜對了,秘密數字是" + yourNum + "! 還要繼續嗎?"); break; } else if (yourNum < ans) { startNum = yourNum; } else { endNum = yourNum; } } } ``` ### 反轉陣列 將一個array內部的所有元素反過來! (禁止使用Array.prototype.reverse()。另外,考慮時間複雜度,需要在O(n)內,不能是O(n^2)) 例如: `const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"];` 會變成 `["Grace", "Mike", "Snap","Ron","Harry"];` ```javascript const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"]; let a = 0, b = friends.length - 1; while (a < b) { let tmp; tmp = friends[a]; friends[a] = friends[b]; friends[b] = tmp; a++; b--; } console.log(friends) ``` ```javascript const friends = ["Harry", "Ron", "Snap", "Mike", "Grace"]; const reversed_friends = []; for (let i = friends.length - 1; i >= 0; i--) { reversed_friends.push(friends[i]); } ``` ### 找出最大值 在這個coding練習裡面,你要寫一個函式,他可以接受一個parameter,參數是一個array of numbers。這個函式要return一個值,也就是Array當中最大的數! 若是 input是一個 empty array,則return undefined。例如: ```javascript findBiggest([15, 20, 22, 16, 7]); // return 22 findBiggest([1, 2, 3, 4, 5, 999]); // return 999 findBiggest([-11, 0, -3, -4, -5, -999]); // return 0 findBiggest([]); // return undefined ``` ```javascript function findBiggest(arr) { console.log('start:' + arr) if (arr.length == 0) { return undefined; } let max = arr[0]; for (let i = 0; i < arr.length; i++) { console.log("compare(max, arr[i]): " + "(" + max + "," + arr[i] + ")") if (max < arr[i]) { console.log('change: max' + arr[i]) max = arr[i]; } } return max; } ``` ### 數值加總 addUpTo() 這個函式有大於0的參數 n,n為一個正整數。 addUpTo() 要 return 1 + 2 + 3 + ..... + n 的值。 例如: ```javascript // addUpTo(5) = 1 + 2 + 3 + 4+ 5 = 15 // addUpTo(100) = 1 + 2 + 3 + 4 + 5 + ....... + 100 = 5050 addUpTo(5); // 15 addUpTo(100); // 5050 addUpTo(5000); // 12502500 addUpTo(100000); // 5000050000 ``` ```javascript function addUpTo(num) { let sum = 0; for (let i = 1; i <= num; i++) { sum += i; } return sum; } ``` # 程式練習題 [題目連結](https://www.notion.so/yuhsien/179caa9730284da48b0a6df66886a447) # CH7 Document Object Model (DOM) ## DOM & Window Object 文件物件模型(DOM)是HTML的程式介面,允許在JS當中操作HTML元素。 * DOM提供了一個文件的結構化(Tree)表示法,讓程式可以存取並改變文件架構、風和、內容。 * DOM提供了文件已擁有屬性與函式的節點與物件組成的結構化表示。 * 節點可以附加事件處理程序,一旦樹發事件就會執行處理程序。 * 本質上,他將網頁與腳本或程式語言連在一起。 ### Window Object `this` -- 1. JS物件method 2. window object `window.alert()` -- alert function is a method of window object `window.prompt()` `window.addEventListen()` -- 將事件監聽程式碼附加到window object `window.setInterval()` -- 每次經過給定的毫秒數時安排一個函數執行 `window.clearInterval()` -- 將`setInterval()`所重複執行的程式暫停 > window object用的時候可以省略window ```javascript function sayHelloToUser() { alert("3秒過了...") } let interval = window.setInterval(sayHelloToUser, 30000); window.clearInterval(interval); ``` > 物件導向程式概念: 一個object可以是另一個object的attribute。 ```javascript let Grace = { name: "Grace", age: 20 }; let Wilson = { name: "Wilson", age: 25, spouse: Grace }; console.log(Wilson.spouse.age); // 20 ``` Window Object可使用的常見properties: * `window.console` -- retun一個concole object,可以對瀏覽器的debugging console進行控制與訪問。常用的method為`log()`, `error()` * `window.document` -- return window包含的文檔(HTML document object),也就是html文件 * `window.localStorage` -- return一個local stage物件 * `window.sessionStorage` -- return一個session stage物件 ### Document Object Model 從DOM可知HTML也被視為是個物件。這個架構被稱之為是Model。 1. doucument是一個object,是window object的一個屬性 2. doucument是指HTML doucument 3. 這個模型意味著所有doucument內部的HTML元素都是object (每個HTM tag都有其屬性和方法) 每個點都是一個節點(node) ![image](https://hackmd.io/_uploads/BJ7pQ5FIp.png) ## get element by id or class 在DOM這棵樹上,每個點被稱為節點(nodes),分為三種: 1. HTML元素節點(element nodes or element object) 2. 文字節點(text node) 3. 註解節點(comment node) DOM提供2種節點集合(not array): 1. HTML collection (only element nodes) 2. NodeList (所有3種nodes) ## Document object Document object指的是`window.document`下的屬性,常見的method: > 注意回傳的data type * `window.document.addEventListener()` * `window.document.createElement(tagName)` * `window.document.getElementById(id)` -- return第一個id相符合的element object * `window.document.getElementByClassName(className)` -- return一個動態的HTML collection,內部元素包含所有給定className的元素 * `querySelector(selectors)` -- return document第一個符合特定選擇器群組的element object,採用深度優先搜尋演算法 * 深度優先搜尋演算法: 走得越深越好,逐一走過每個tag去尋找 * `querySelectorAll(selectors)` -- return一個靜態(not live) NodeList,表示與指定選擇器組匹配的元素列表 > querySelector在現在的專案中比較常使用,使用CSS的selector > CSS selector: id - `#id`, class - `.class` ### HTML collection & NodeList 注意: HTML collection, NodeList are not array (cannot use `pop`, ...) #### HTML collection 是一種array-like object [HTMLCollection](https://developer.mozilla.org/en-US/docs/Web/API/HTMLCollection) -- 動態 dynamtic,會因為DOM改變而length會跟著改變 #### NodeList [NodeList](https://developer.mozilla.org/zh-TW/docs/Web/API/NodeList) -- 靜態 static,不會因為DOM改變而length會跟著改變,改變DOM之後再重新取得一次 ### 差別比較 #### ==childNodes & children== Element Object是三種節點的其中一種 * 每個節點Node有`childNodes`屬性,return type為NodeList,包含了此節點在DOM Tree之下的第一層的所有節點 * 每個Element Object有`children`屬性,return type為HTML collection,包含了此節點在DOM Tree之下的第一層的所有HTML Object Element Object這種node可以使用`childNodes`&`children`屬性,但另外兩種nodes(text, comment)只有能夠使用`childNodes`,若使用`children`則return `undefined`。 #### JS取得html元素方法 | Methods | return type | | ---------------------------- |:---------------------------------------- | | getElementById(id) | Element object | | getElementByClassName(class) | HTMLCollection(內部元素為Element object) | | querySelect(selector) | Element object | | querySelectAll(selector) | NodeList(內部元素為Nodes) | #### NodeList vs HTMLCollection ![image](https://hackmd.io/_uploads/SJRSNjFU6.png) ```javascript let hellos = document.querySelectorAll(".hello"); hellos.forEach((hello) => { console.log(hello); }) let helloss = document.getElementsByClassName("hello"); helloss.forEach((hello) => { console.log(hello); // Uncaught TypeError: helloss.forEach is not a function }) ``` ## Function Declaration & Expression 在JS中定義function有兩種方式: * Declaration -- `function funcName (para){}` * Expression -- `function (para){}`,省略function name來創建匿名函數 ### Function Expression用途 * 定義沒有名稱的函數放入變數中,讓程式碼更有彈性。PS.與Declaration幾乎沒有區別,但若使用Declaration,可在定義函數前使用該function ```javascript let Wilson = { name: "Wilson", greet() { console.log(this.name + "打招呼") }, walk: function () { console.log(this.name + "正在走路") } }; console.log(Wilson.walk()); //Function Expression console.log(Wilson.greet()); ``` * 作為higher order function的callback function使用,ex. forEach, addEventListener > higher order function: 在function中放入function,A(B),A: higher order function 優點: 1. 若程式碼有許多callback function都是使用function declaration,命名變數時都需要避開function declaration 2. callback function名稱其實沒有意義 3. 程式碼變乾淨 ```javascript function react() { alert("有人正在點擊畫面"); } // parameter 1: 是哪個event // parameter 2: 事件發生後,我們要執行的function // addEventListener 本身是一個higher order function // react是一個callback function window.addEventListener("click", react); // Function Expression window.addEventListener("click", function () { alert("有人正在點擊畫面"); }); ``` * 使用IIFE(Immediately Involied Function Expression)的功能 在伺服器的程式碼可以使用(打開後就立即要執行) ```javascript (function (a, b) { console.log(a + b); })(10, 5); ``` ### Arrow Function Expression JS中的函數是first-class object(一等公民),可以: 1. 將function分配給變數: `let hello = function(){};` 2. 將function當作argument傳給其他function(=數學的composite function)。higher order function的argument被稱為callback function。 在許多情況下,若要把function當作argument傳給其他function,那麼對此函數命名則變的沒有意義,建議使用function expression。JA中Arrow Function Expression是function expression的簡化語法。 * `() => ...` ```javascript () => { console.log("hello"); } let Wilson = { name: "Wilson", greet() { console.log(this.name + "打招呼") }, // arrow function express沒有this keyword綁定 walk: () => { console.log(this); // {} 空物件 console.log(this.name); // undefined console.log("Wilson is walking...."); } }; ``` * `param => ...` * `(param1, param2, ...) => ...` #### 總結 1. 若有一個參數,則不需要加括號;若有0或2 up的參數,則需要加括號 2. ...(expression)無加大括號(curly brackets),會自動return (only一個計算式) ```javascript let num1 = () => 15; // 15 let num2 = () => { 15 }; // undefined let num3 = () => { return 15 }; // 15 let addition1 = (a, b) => a + b; console.log(addition1(10, 30)); // 30 let addition2 = (a, b) => { let pi = 3.14; return a * b * pi; }; console.log(addition2(10, 30)); // 30 ``` 3. **Arrow Function Expression沒有this關鍵字綁定,不應該用作objects的method** ### forEach() Method forEach()為higher order function,與arrow function expression協作為: * `forEach(element => ...)` * `forEach(callbackFn)` * `forEach((element, index) => ...)` ```javascript let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7]; // 1. for迴圈 for (let i = 0; i < myLuckyNumbers.length; i++) { console(myLuckyNumbers[i] + 3); } 2. 使用funciton declaration function plus3(n) { console(n + 3); } myLuckyNumbers.forEach(plus3); // 3. 使用function expression創建匿名函數 myLuckyNumbers.forEach(function (n) { console(n + 3); }); // 4. arrow function expression myLuckyNumbers.forEach((n) => { console(n + 3); }); // 搭配index myLuckyNumbers.forEach((n, index) => { console(n + " is at index " + index); }); ``` ## Element objects 在DOM Tree中,每個HTML元素可能有自己獨特的properties和methods,eg. `<video>`, `<h1>`...。 除了各自的properties, methods外,也有共用的properties, methods: * `addEventListener(event, callbackFn)` ```javascript let myBtn = document.querySelector("#my-btn"); myBtn.addEventListener("click", () => { alert("你點了btn"); }) ``` * `appendChild(element)` ```javascript let body = document.querySelector("body"); let myH1 = document.createElement("h1"); body.appendChild(myH1); ``` * `innerHTML` * `innerText` ```javascript let body = document.querySelector("body"); let myH1 = document.createElement("h1"); //innerHTML: 會被當作HTML的程式理解,可以插入tag, innerText: 只會被當作文字 myH1.innerText = "<a>這是我的h1</a>"; myH1.innerHTML = "<a href='https://www.google.com'>這是我的h1</a>"; body.appendChild(myH1); ``` * `children` -- return HTMLcollection * `childNodes` -- return NodeList * `parentElement` -- 選取父元素 * `classList` -- element class的列表,可以用的method有`add()`, `remove()`, `toggle()`, `contains()` * `toggle()` -- 有屬性的話刪除,沒有的話則加入 * `contains()` -- 檢查是否有指定的class * `getAttribute(attributeName)` -- 獲得一個屬性,eg. href ```javascript <a title="到google首頁" href="https://www.google.com"></a> let a = document.querySelector("a"); a.getAttribute("title"); a.getAttribute("href"); ``` * `querySelector(selector)` -- document object也可以用,但element object使用只找element底下的 * `querySelectorAll(selector)` -- document object也可以用,但element object使用只找element底下的 * `remove()` -- 直接移除此tag (not hidden) ```javascript <button>讓anchor tag消失</button> <a title="到google首頁" href="https://www.google.com"></a> let button = document.querySelector("button"); button.addEventListener("click", ()=>{ let a = document.querySelector("a"); a.remove(); }) ``` * `style` -- 可以用來改變element object的inline style,因為js中不能使用hyphen`-`,因此JS中的CSS屬性都被更改為cameCase ```javascript let button = document.querySelector("button"); button.style.backgroundColor = "green"; button.style.color = "white"; button.style = "background-color: green; color: white;"; ``` ## Inheritance 繼承 可以將attribute, methods從一個class繼承到另一個class。 * subclass(子類), child class -- 從一個class繼承的class * superclass(父類) -- 繼承自的class > object B -> object A: B為subclass, A為superclass 優點: 1. 減少code, RAM下降, 提升速度 2. 較好維護 所有的html元素都從element object繼承了peoperties, methods,其中某些HTML元素有自己獨特的peoperties, methods。 ![image](https://hackmd.io/_uploads/HywTauWwT.png) ## JavaScript Events 表示一個在DOM物件所發生的事件,事件通常由使用者的操作行為所產生(ex: 調整螢幕大小、點滑鼠、敲鍵盤...)。 `addEventListener(type, listener)` - 等待事件發生,去執行被賦予的func。 * `type`: 事件類型,eg. click, resize... * `listener`: callback function,被執行時,js會把event object (`e`)當作argument,放進listener執行 ```javascript let button = document.querySelector("button"); button.addEventListener("click", (e)=>{ console.log(e); }) ``` ### JS Event繼承關係 ![image](https://hackmd.io/_uploads/B17GrwGPa.png) [Event reference list MDN](https://developer.mozilla.org/zh-TW/docs/Web/Events#%E4%BA%8B%E4%BB%B6%E5%88%86%E9%A1%9E) ```javascript window.addEventListener("keydown", (e) => { console.log(e); // keycode: 按的鍵 }) ``` ### event object常用屬性、方法 * `target` -- 指向最初觸發事件的DOM物件 * `preventDefault()` -- 取消事件的預設行為,但部會影響事件的傳遞,事件仍會繼續傳遞 * `stopPropagation()` -- 可以防止event bubbling進一步傳播當前事件 ```javascript <input type="text" /> <input type="number" min="0" max="10" /> <button>交出表單</button> let button = document.querySelector("button"); button.addEventListener("submit", (e) => { e.preventDefault(); // 取消交出表單的預設行為 }) ``` ## Event Bubbling 冒泡事件 當一個事件發生在一個元素上時,他首先在其上運其event handler,然後運行其parent element的event handler,然後一職網上運行其他祖先的event handler。 > In programming, an event handler is a callback routine that operates asynchronously once an event takes place. ```javascript <style> .a { width: 300px; height: 300px; background-color: red; } .b { width: 150px; height: 150px; background-color: blue; } </style> <div class="a"> <div class="b"></div> </div> let a = document.querySelector("a"); let b = document.querySelector("b"); a.addEventListener("click", () => { alert("紅色框的事件監聽器正在被執行"); }) b.addEventListener("click", () => { e.stopPropagation(); // 防止冒泡事件 alert("藍色框的事件監聽器正在被執行"); }) ``` ## Local Storage & Session Storage Storage是瀏覽器儲存數據的地方,並非database。在Storage內部儲存的數值都是key-value pair,且key, value都會被轉為string。 * Local Storage: 沒有過期的時效 * Session Storage: 用戶關閉瀏覽器後,sessionStorage就會被銷毀 localStorage和sessionStorage使用的methods是一樣的: * `setItem(key, value)` -- 當傳遞一個key和value時,會將該key-value pair加到給定的storage,或是該key的值已存在,則更新key的value * `getItem(key)` -- 給定key取得value,若值不存在,則return `null` ```javascript localStorage.setItem("name", "wilson"); localStorage.setItem("age", 26); let myAge = localStorage.getItem("age"); console.log(myAge); ``` * `removeItem(key)` -- 從給定的storage中刪除該key-value pair * `clear()` -- 清除儲存在給定storage中的所有key-value pair Storage bind 網址 ![image](https://hackmd.io/_uploads/BJBxdOzvT.png) ### JSON JSON(JavaScript Object Notation),是一種交換資料的格式。JSON Object有兩個method: * `JSON.stringify(value)` -- 將value換成JSON String * `JSON.parse(text)` -- 解析JSON String,製作出JSON String描述的JavaScript值或是Object array -> JSON.stringify => JSON String JSON String -> JSON.parse => array ![image](https://hackmd.io/_uploads/HJhn9uMwa.png) ```javascript let myLuckyNumbers = [1, 2, 3]; localStorage.setItem("myLuckyNumbers", JSON.stringify(myLuckyNumbers)); let myNumber = JSON.parse(localStorage.getItem("myLuckyNumbers")); console.log(typeof myNumber); myNumber.forEach(n => { console.log(n) }); ```