# 一些複雜的觀念釐清與小技巧 > 前言:以下寫到的東西大多是課堂上帶到,但老師可能來不及補充得更詳細的部分 > > JavaScript 有很多基礎觀念,當初自學的時候我原本覺得這些東西其實不太重要,但實際上這些觀念會影響你的程式碼夠不夠嚴謹、漂亮,以及基礎夠不夠紮實(前公司的後端寫了好幾年還在用 var 跟 callback 處理非同步真的差點摔鍵盤) > > 因為 hackmd 可以共同編輯,所以如果有地方觀念轉不過來歡迎告訴我!!!費曼學習法!!! > > 解釋的過程中如果有名詞聽不懂,直接告訴我,我會為了你再開一個區塊解釋 <3 *** ## CSS ### CSS 變數 #### 宣告 ```css :root { --main-color: #665544 /* 宣告 CSS 變數 */ } ``` #### 使用 ```css div { background-color: var(--main-color) /* 使用 CSS 變數 */ } ``` #### 以 JavaScript 改變 ```javascript= document.querySelector(":root").setProperty("--main-color", "#000000"); // 以 JavaScript 改變 --main-color 的值 ``` ## JavaScript ### 立即執行的函式 IIFE: Immediately Invoked Function Expression ```javascript= // 首先如果有個函式我們需要使它立即執行,需要這樣做 function func1(a) { console.log(a); } func1(1); // IIFE 的寫法,可以在宣告函式的同時使其執行(注意括號的位置!) (function func2(a) { console.log(a); })(1); ``` ### 省略 document.querySelector 的宣告 ```html <input id="hello1" value="hello1" /> <input id="hello2" value="hello2" /> ``` ```javascript= // 目前我們用來取得 input#hello1 值的方式 var input_hello1 = document.querySelector("#hello1"); var value = input_hello1.value; console.log(value); // 印出 hello1 // 但其實如果該元素有寫 ID,JavaScript 會自動幫我們找到它 console.log(hello2.value); ``` ### 箭頭函式 Arrow function > 箭頭函式 () => {} 的 this 與一般函式的 function() {} 不太相同,這部分我先略過 > 再提醒一下,如果你在事件監聽器 addEventListener 中會使用 this 時,不要使用箭頭函式,會導致你的 this 指向全域 window 物件而非觸發該事件的元素本身 ```javascript= // 寫法 1 - 一個參數,無回傳(return)值 function func3(a) {} var arrow_func3 = (a) => {}; // 在只有一個參數的情況下,參數的括號可省略寫成 a => {} // 寫法 2 - 兩個參數,無回傳值 function func4(a, b) {} var arrow_func4 = (a, b) => {}; // 一個以上參數的情況,參數的括號不可省略 // 寫法 3 - 兩個參數,只會回傳值 function func5(a, b) { return a+b; } var arrow_func5 = (a, b) => a+b; // 如果函式內只有 return,可省略外部的大括號 {} // 寫法 4 - 一個參數,會執行一些動作才回傳值 function func6(a, b) { console.log(a, b); return a+b; } var arrow_func6 = (a, b) => { // 如果除了 return 之外有其它動作,就與一般的函式相同,以 {} 圍住函式主體 console.log(a, b); return a+b; }; ``` ### 事件的傳遞機制:捕獲與冒泡 Capturing & Bubbling > 直接貼[網址](https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/),這篇講得很詳細!!! > 可能會有人問「這到底能幹嘛?」,善用這個機制可以讓你在這次的增刪查改作業中,不需要一直對刪除按鈕重新綁定監聽事件 ```html <table> <tbody id="tbody"> <tr> <td> 我是 td <button>按我!</button> </td> </tr> </tbody> </table> ``` ```javascript // 舉例來說,我把監聽點擊刪除按鈕的事件綁到 tbody 而非每個 button 身上 // 這樣子的好處是就算我的 button 一直被刪除再重新放到畫面上,都不需要重新綁定事件監聽器 tbody.addEventListener("click", () => { // tbody 底下元素的所有點擊事件,都可以透過父元素 tbody 的監聽器捕捉! console.log(event.target); // 此時的 event.target 會指向被點擊的元素 }); ``` ### data-attribute > 在 HTML 中放入自定義的屬性並使用 JavaScript 取值 ```html <!-- 先在 HTML 中放入一個帶有三個自定義屬性的按鈕 --> <button id="btn" data-morning="1" data-evening="2" data-night="3"> ``` ```javascript= /* 如果我們想取得 data-morning/evening/night 三個屬性的值 */ // 這是課堂上老師使用的方式 var morning = btn.getAttribute("data-morning"); var evening = btn.getAttribute("data-morning"); var night = btn.getAttribute("data-night"); console.log(morning, evening, night); // 印出 1 2 3 // 但其實有個更簡單的方式 console.log(btn.dataset); // => {morning: "1", evening: "2", night: "3"} // 因為 btn.dataset 是物件,當然也能用物件的方式直接取值 console.log(btn.dataset.morning); // => 1 console.log(btn.dataset["evening"]) // => 2 ``` ### 解構賦值 Destructuring assignment > #### 陣列的解構 ```javascript var arr = [1,2,3]; // 先宣告一個陣列 console.log(a, b, c); // 題目:如何使 a, b, c 分別印出陣列中的元素? ``` ```javascript // 你可能會想到的做法 var a = arr[0], b = arr[1], c = arr[2]; // 對陣列使用解構賦值時,等號左方相對位置的變數會被賦予等號右邊陣列內相同位置的元素值,如果變數無法對應則值被設為 undefined var [a, b, c, d] = arr; // d = undefined ``` ```javascript // 如果想跳過陣列中的第一個元素,則逗點前不輸入變數 var [, b, c] = arr; ``` #### 物件的解構 ```javascript var obj = { x: 1, b: 2, c: 3 }; // 先宣告一個物件 ``` ```javascript // 對物件使用解構賦值時,等號左邊的變數名稱會自動尋找等號右邊物件中的對應屬性;如果變數無法對應則值被設為 undefined var { a, x } = obj; // a = undefined // 在對物件使用解構時,可以同時賦予新的變數名稱 var { x: hello } = obj;; // x = undefined, hello = 1 ``` ### 展開運算子 Spread Operator 與其餘運算子 Rest Operator > 展開運算子與其餘運算子的共通寫法都是`...`,但使用情境有差異 > 展開運算子:從陣列中取出所有元素 > 其餘運算子:將剩餘元素轉成陣列 ```javascript // 展開運算子 var arr1 = [1, 2]; var arr2 = [4, 5]; var arr3 = [...arr1, ...arr2]; console.log(arr3) // [1, 2, 4, 5] ``` ```javascript // 其餘運算子 var arr = [1, 2, 3]; var obj = { x: 1, y: 2, z: 3 }; // 使用情況,解構的時候想把剩下的元素存進一個陣列中 var [a, ...b] = arr; // a = 1, b = [2, 3] // 同樣也能對物件使用 var { x, ...test } = obj; // test = { y:2, z: 3 } ``` ### 再把物件講清楚一點 ```javascript= var human = { height: 175, greet: function() { console.log("hello!"); } } // 在上述的例子中,height 與 greet 都是物件的屬性(property),但因為 greet 後方為函式,所以也會稱 greet 為物件的方法(method) human.greet(); // 印出 hello! // 知道物件可以定義方法很重要,最近用的 fetch.then() 也是呼叫物件方法的其中一個例子 ``` ### 關於 this ### var, let 與 const > 區塊指的是程式碼中的大括號 `{}` ```javascript // var 的作用域(scope)位於函式區塊(與其子區塊)中 (function() { { var v1 = "var_1"; } console.log(v1); // "var_1" })(); for (var v2=0; v2<10; v2++) {} console.log(v1); // not defined;因為變數被宣告在另一個函式中 console.log(v2); // 10;for 並非一個函式 ``` ```javascript // let 的作用域(scope)位於區塊(與其子區塊)中,比 var 更小 (function() { { let l1 = "let_1"; } console.log(l1); // not defined })(); for (let l2=0; v2<10; v2++) {} console.log(l1); // not defined console.log(l2); // not defined ``` ```javascript // const 作用域與 let 相同,唯一的差別是一旦宣告後就無法再改變(const 即為常數 constant 的縮寫) const c1 = "this"; c1 = "that"; // TypeError: Assignment to constant variable.(類型錯誤:試圖指派新的值給常數變數) console.log(c1); // "this"; ``` ### 什麼是 callback function(回呼函式?) ### 什麼是同步(synchronous)與非同步(asynchronous)? #### 非同步解決方案 ##### callback ##### ES6 Promise ##### ES7 async/await