JavaScript 相關

Posted by Adam on August 24, 2022
# 📘 Document `document` 是 DOM 操作的入口,在純 JavaScript 裡操作 HTML 的核心物件。除了 `querySelector` 與 `createElement`,以下是**最常用也最實用的 `document` 方法與屬性**,我依照用途幫你分類: --- ## 🔍 元素選取(DOM 查詢) | 方法 | 說明 | | ----------------------------------- | --------------------------------- | | `getElementById(id)` | 用 `id` 取得單一元素(老方法,但仍實用) | | `getElementsByClassName(className)` | 取得有指定 class 的所有元素(HTMLCollection) | | `getElementsByTagName(tagName)` | 取得指定 tag 的所有元素(HTMLCollection) | | `querySelector(selector)` | 取得符合 CSS 選擇器的**第一個**元素 | | `querySelectorAll(selector)` | 取得所有符合的元素(NodeList) | --- ## 🧱 元素建立與節點操作 | 方法/屬性 | 說明 | | ---------------------------------------------- | ---------------------------------------- | | `createElement(tag)` | 建立新元素,例如 `document.createElement('div')` | | `createTextNode(text)` | 建立文字節點(用於插入純文字) | | `importNode(node, deep)` | 匯入其他 document 的節點 | | `fragment = document.createDocumentFragment()` | 建立「虛擬 DOM」暫存節點,效率高於直接插入 | | `document.body`, `document.head` | 直接取 `<body>` 或 `<head>` 節點 | | `document.documentElement` | 取得 `<html>` 根節點 | --- ## 📄 文件與事件資訊 | 屬性/方法 | 說明 | | ------------------------------------- | -------------------------------------- | | `document.title` | 網頁標題,可讀可寫 | | `document.URL` | 當前網址(唯讀) | | `document.readyState` | 加載狀態(loading / interactive / complete) | | `document.addEventListener(type, fn)` | 加事件監聽,像 `DOMContentLoaded` | | `document.removeEventListener(...)` | 移除事件監聽 | --- ## 📋 複製/剪貼簿(Clipboard) | 方法 | 說明 | | ------------------------------------- | ----------------------------- | | `execCommand('copy')` *(舊)* | 嘗試觸發複製(已過時,建議用 Clipboard API) | | `navigator.clipboard.readText()` | 讀取剪貼簿內容(async) | | `navigator.clipboard.writeText(text)` | 將文字寫入剪貼簿 | --- ## 🧪 其它有用方法 | 方法/屬性 | 說明 | | ----------------------------------------------- | ------------------ | | `element.contains(child)` | 判斷某元素是否包含另一元素 | | `element.cloneNode(deep)` | 複製節點(`true`=含子孫) | | `element.remove()` | 從 DOM 移除元素 | | `element.append(...)` / `prepend(...)` | 插入元素或文字(可多個) | | `element.innerHTML`, `textContent`, `outerHTML` | 操作 HTML/純文字/整個元素字串 | --- ## 🧠 小提醒:DOM 節點操作也很多是在 `element` 上 例如: ```js const div = document.createElement('div'); div.classList.add('box'); div.textContent = 'Hello'; document.body.appendChild(div); ``` 這些像 `classList`, `textContent`, `appendChild`, `removeChild` 都是在元素物件上(非 `document` 本身)。 ### 使用 `insertAdjacentElement` 將元件串接在後 ```js const btn3 = document.getElementById('myBtn'); const newBtn3 = document.createElement('button'); newBtn3.id = 'newBtn3'; newBtn3.textContent = '新增按鈕 C'; // 直接把 newBtn3 插到 btn3 旁邊(afterend) btn3.insertAdjacentElement('afterend', newBtn3); ``` --- --- # 📘 Regex ```js // 將 "world" 替換為 "universe" var str = "Hello, world!"; var newStr = str.replace(/world/, "universe"); // 使用正則表達式進行替換 console.log(newStr); // "Hello, universe!" // 替換所有數字為 "#" var text = "12345 and 67890"; var replacedText = text.replace(/\d/g, "#"); console.log(replacedText); // "##### and #####" // 將 yyyy-mm-dd 格式的日期轉換成 mm/dd/yyyy 格式 var dateStr = "2023-05-25"; var formattedDate = dateStr.replace(/(\d{4})-(\d{2})-(\d{2})/, "$2/$3/$1"); console.log(formattedDate); // "05/25/2023" // 首字字母大寫,其餘小寫 function format(str) { return str.toLowerCase().replace(/^\w/, (c) => c.toUpperCase()); } ``` 在 JavaScript(以下簡稱 “JS”)裡,正則表示式(RegExp)主要有兩種宣告方式,並搭配一系列常用的標誌(flag)與語法結構。以下分段說明並輔以範例。 一、宣告方式 1. 文字(Literal)語法 let re = /pattern/flags; 範例: ```js const re1 = /\d{3}-\d{4}/g; // 尋找「3 位數字 + ‘-’ + 4 位數字」,全域搜尋 ``` 2. 建構子(Constructor)語法 let re = new RegExp("pattern", "flags"); 範例: ```js const re2 = new RegExp("\\d{3}-\\d{4}", "g"); // 在字串中,\ 要再多加一層跳脫 ``` 二、常用標誌(Flags) - g (global):全域搜尋,不只第一筆 - i (ignore case):不分大小寫 - m (multiline):多行模式,^、$ 可在每行起/尾匹配 - u (unicode):啟用 Unicode 支援(例如 u+{1F601}) - s (dotAll):讓 . 能匹配換行符(\n) - y (sticky):「黏貼」模式,從 lastIndex 精確位置開始匹配 三、核心語法元件與範例 1. 字元類別(Character Classes) - \d:數字等價於 [0-9] - \D:非數字等價於 [^0-9] - \w:英文數字底線 [A-Za-z0-9_] - \W:非 \w - \s:空白字元 [\t\n\r\f\v] - . :除換行以外的任意字元(s 標誌下可包含換行) 範例: ```js /\w+@\w+\.\w{2,3}/i // 電子郵件簡易匹配 ``` 2. 量詞(Quantifiers) - X*:0 次或多次 - X+:1 次或多次 - X?:0 次或 1 次 - X{n}:剛好 n 次 - X{n,}:至少 n 次 - X{n,m}:介於 n–m 次 範例: ```js /a{2,4}/ // "aa", "aaa", "aaaa" 都可 ``` 3. 邊界與定位符(Anchors) - ^:字串或行首 - $:字串或行尾 - \b:單字邊界 - \B:非單字邊界 範例: ```js /^Hello/ // 以 Hello 開頭 /world!$/ // 以 world! 結尾 /\bcat\b/ // 獨立的 "cat" ``` 4. 群組與選擇 - ( … ):括號群組,默認會記錄(capturing group) - (?: … ):非記錄群組(non-capturing) - |:或(alternation) 範例: ```js /(dog|cat)s?/ // dog, dogs, cat, cats /(?:Mr|Mrs)\.?\s[A-Z][a-z]+/ // 非捕獲:Mr. Smith, Mrs Jones ``` 5. 前瞻與後顧(Lookaround) - (?= … ):正向前瞻 - (?! … ):負向前瞻 - (?<= … ):正向後顧 (ES2018+) - (?<! … ):負向後顧 (ES2018+) 範例: ```js /\d+(?=%)/ // 匹配緊跟百分比符號前的數字 /(?<=\$)\d+/ // 匹配美元符號後的數字 ``` 四、常用搭配方法 1. RegExp 物件方法 - test(str):回傳 true/false - exec(str):回傳第一組匹配結果(含群組),無則 null 範例: ```js const re = /cat/g; console.log(re.test("a cat")); // true console.log(re.exec("a cat")); // ["cat", index:2, …] ``` 2. String 物件方法 - match(re):回傳所有匹配(若無 g,則回傳第一組) - replace(re, repl):替換匹配 - search(re):回傳第一個匹配的索引 - split(re):以正則為分隔符拆分字串 範例: ```js "a1b2c3".match(/\d/g); // ["1","2","3"] "foo".replace(/o/g, "0"); // "f00" "hello".search(/l+/); // 2 "a,b;c".split(/[,;]/); // ["a","b","c"] ``` 五、小結 - 宣告:Literal (/…/flags) 或 Constructor - 標誌:g, i, m, u, s, y - 元件:字元類別、量詞、錨點、群組、前瞻/後顧 - 常用方法:test, exec, match, replace, search, split 多多練習、並利用線上工具(如 regex101.com)測試,即可快速上手!如果有特定範例需求,歡迎再詢問~ --- --- # 📘 字串處理 ```js var mainString = "Hello, world!"; var subString = "world"; console.log(mainString.length); // 字串長度:13 console.log(subString.length); // 字串長度:5 if (mainString.includes(subString)) { console.log("字串包含指定的子字串"); } else { console.log("字串不包含指定的子字串"); } var sentence = "This is a sample sentence."; var words = sentence.split(" "); // 將句子按空格切割成單字陣列 console.log(words); // ["This", "is", "a", "sample", "sentence."] var originalStr = "Hello, world!"; var subStr1 = originalStr.substring(0, 5); // 從索引 0 到索引 4 的子字串 var subStr2 = originalStr.substr(7, 5); // 從索引 7 開始,取 5 個字元的子字串 console.log(subStr1); // "Hello" console.log(subStr2); // "world" ``` ### 模板字串(Template literals): 使用模板字串可以更清晰地格式化字串,通過在字串周圍使用反引號,全在字串中使用${}來插入變數。例如: ```js var name = "John"; var age = 30; var formattedString = `My name is ${name} and I am ${age} years old.`; console.log(formattedString); ``` --- --- # 📘 Selector ## ✅ `document.querySelectorAll(selector)` **功能**: 從整個網頁(`document`)中,根據 CSS 選擇器選取「所有符合條件的元素」。 **回傳值**: 一個 `NodeList`(類陣列),可使用 `forEach` 遍歷。 **語法**: ```js document.querySelectorAll('div.my-class') ``` **範例**: ```html <div class="my-class">A</div> <div class="my-class">B</div> ``` ```js const nodes = document.querySelectorAll('div.my-class'); nodes.forEach(el => console.log(el.textContent)); // 依序輸出 A, B ``` --- ## ✅ `div.querySelector(selector)` **功能**: 從指定的 DOM 節點(如某個 `div`)中,查詢**第一個**符合 CSS 選擇器的「子元素」。 **回傳值**: 一個 `Element` 或 `null` **語法**: ```js const first = div.querySelector('.inner'); ``` **範例**: ```html <div id="container"> <span class="inner">Hello</span> <span class="inner">World</span> </div> ``` ```js const container = document.getElementById('container'); const firstInner = container.querySelector('.inner'); console.log(firstInner.textContent); // Hello(只找第一個) ``` --- ## 🆚 差異總覽 | 比較項目 | `document.querySelectorAll` | `div.querySelector` | | ----- | --------------------------- | ------------------- | | 查詢範圍 | 整個網頁 | 限定某個元素底下 | | 回傳類型 | `NodeList`(所有符合) | 第一個符合的元素 or `null` | | 支援選擇器 | ✅ 支援完整 CSS 選擇器語法 | ✅ 相同 | | 用途 | 全域查詢 | 區域查詢 | --- ## 👀 延伸補充 | 方法 | 說明 | | -------------------------------------- | ------------ | | `document.querySelector('selector')` | 全頁中找第一個符合的元素 | | `element.querySelectorAll('selector')` | 在某元素下找所有符合的 | --- ## ✅ 常見選擇器語法 以下是常見的 CSS 選擇器類型,均可用於 `querySelector` / `querySelectorAll`: | 語法 | 說明 | 範例 | | ---------------- | --------- | ------------------------------------ | | `tag` | 標籤名稱 | `'div'`、`'input'` | | `.class` | 類別 | `'.my-class'` | | `#id` | ID | `'#my-id'` | | `[attr]` | 屬性存在 | `'[disabled]'`、`'[type]'` | | `[attr=value]` | 屬性等於值 | `'[type="text"]'`、`'[name="email"]'` | | `parent child` | 子層級 | `'ul li'`、`'#form input'` | | `parent > child` | 直接子層級 | `'form > input'` | | `A + B` | 緊鄰的兄弟元素 | `'label + input'` | | `A ~ B` | 所有後面的兄弟元素 | `'h2 ~ p'` | | `:first-child` | 第一個子元素 | `'li:first-child'` | | `:last-child` | 最後一個子元素 | `'tr:last-child'` | | `:nth-child(n)` | 第 n 個子元素 | `'li:nth-child(2)'` | | `:not(selector)` | 排除選擇器 | `'input:not([type="submit"])'` | --- ## ✅ 範例說明 ```js // 取得所有 class 為 "box" 的 div document.querySelectorAll('div.box'); // 取得 id 為 "main" 底下的第一個 input document.querySelector('#main input'); // 取得所有 type 為 checkbox 的 input document.querySelectorAll('input[type="checkbox"]'); // 取得 class 為 .btn 且未 disabled 的按鈕 document.querySelectorAll('button.btn:not([disabled])'); ``` --- ## ⚠️ 注意事項 1. 傳入的字串必須是 **合法的 CSS 選擇器**,否則會拋出錯誤(SyntaxError)。 2. 如果選擇器中有特殊字元(如空白、點號),記得要用引號包起來。 ```js // 1. 建立一個 <textarea> 元素 var ta = document.createElement('textarea'); // 2. 設定屬性 ta.id = 'myTextarea'; ta.rows = 5; ta.cols = 40; ta.placeholder = '在此輸入內容…'; // 3. 新增到 body 底部 document.body.appendChild(ta); ``` --- --- # 📘 JSON --- ## 1. `JSON.stringify` — 將 JavaScript 物件轉成 JSON 字串 * **用途**:把 JS 物件、陣列等,轉成符合 JSON 格式的字串(文字)。 * **用途場景**:要把資料存成字串(例如存在 localStorage、送到後端 API) ### 範例 ```js const obj = { name: "Adam", age: 30, hobbies: ["coding", "music"] }; // 轉成 JSON 字串 const jsonStr = JSON.stringify(obj); console.log(jsonStr); // 輸出: '{"name":"Adam","age":30,"hobbies":["coding","music"]}' console.log(typeof jsonStr); // "string" ``` --- ## 2. `JSON.parse` — 將 JSON 字串轉回 JavaScript 物件 * **用途**:把 JSON 格式的字串,轉成可用的 JS 物件或陣列。 * **用途場景**:收到 API 回傳 JSON 字串,或從 localStorage 讀取的字串資料。 ### 範例 ```js const jsonStr = '{"name":"Adam","age":30,"hobbies":["coding","music"]}'; // 轉回 JS 物件 const obj = JSON.parse(jsonStr); console.log(obj.name); // Adam console.log(obj.hobbies[1]); // music console.log(typeof obj); // "object" ``` --- ## 注意事項 * `JSON.parse` 的輸入必須是**合法的 JSON 字串**,否則會拋錯。 * `JSON.stringify` 只能轉換 JS 可序列化的資料(函式、`undefined`、Symbol 無法轉換)。 --- ## 綜合範例 ```js const user = { id: 1, name: "Adam", active: true, }; // 物件 → JSON 字串 const json = JSON.stringify(user); // 將 JSON 字串存到 localStorage localStorage.setItem("user", json); // 從 localStorage 讀取 const storedJson = localStorage.getItem("user"); // JSON 字串 → 物件 const parsedUser = JSON.parse(storedJson); console.log(parsedUser.name); // Adam ``` --- --- # 📘 嚴格模式 嚴格模式(Strict Mode)是 JavaScript 在 ES5 以後新增的一種「加強版」執行環境,目的是讓語言更安全、錯誤更早被抓到,也避免一些容易引起混亂的行為。 --- ## 嚴格模式簡介 ### 1. **開啟方式** 在 JS 檔案或函式最頂端加一行: ```js "use strict"; ``` --- ### 2. **主要改變** * 禁止使用未宣告的變數(避免意外創造全域變數) * 函式內的 `this` 若沒指定呼叫對象,會是 `undefined`,而不是全域物件 `window` * 禁止刪除不能刪除的屬性 * 禁止重複參數名稱 * 禁止用 `with` 語句 * 其他讓錯誤更早暴露的限制 --- ### 3. **對 Java 使用者的幫助** * Java 是靜態型別且嚴格的語言,JavaScript 本質比較寬鬆,很容易不小心寫錯不被發現。 * **嚴格模式讓 JS 更嚴謹、更接近 Java 的「錯誤早報」特性**,讓你寫程式時更安心。 * `this` 行為更合理:普通函式沒呼叫對象時 `this` 是 `undefined`,避免你誤用全域物件導致難查錯的 bug。 --- ### 4. **簡單範例** ```js "use strict"; function foo() { console.log(this); } foo(); // undefined(非嚴格模式下是 window) ``` --- --- # 📘 Web Worker 筆記 --- ## ✅ 什麼是 Web Worker? **Web Worker 是一種瀏覽器 API**,可以讓 JavaScript 在主執行緒以外的背景執行緒中執行程式碼,解決 UI 被卡住的問題。 --- ### 🧠 背景:JS 是單執行緒的 * 所有 JS 執行都在「主執行緒」(UI Thread) * 若做大量計算(例如 for-loop、圖片處理、大資料分析),UI 會「卡住」 * Web Worker 讓你把這類運算搬到背景執行,不影響主執行緒 --- ## 🧪 範例 1:用外部 `.js` 檔建立 Worker ### 📄 `index.html` ```html <!DOCTYPE html> <html> <body> <button id="btn">開始運算</button> <p id="result">尚未執行</p> <script> const worker = new Worker("worker.js"); const btn = document.getElementById("btn"); const result = document.getElementById("result"); btn.addEventListener("click", () => { result.textContent = "背景運算中..."; worker.postMessage("start"); }); worker.onmessage = (e) => { result.textContent = "結果:" + e.data; }; </script> </body> </html> ``` --- ### 📄 `worker.js` ```js onmessage = function (e) { if (e.data === "start") { let sum = 0; for (let i = 0; i < 1e9; i++) { sum += i; } postMessage(sum); } }; ``` --- ## 🧪 範例 2:用 `Blob` 建立 Inline Worker(無需額外 `.js` 檔) ```html <script> const workerCode = ` self.onmessage = function (e) { if (e.data === "start") { let sum = 0; for (let i = 0; i < 1e9; i++) { sum += i; } self.postMessage(sum); } }; `; const blob = new Blob([workerCode], { type: "application/javascript" }); const worker = new Worker(URL.createObjectURL(blob)); worker.postMessage("start"); worker.onmessage = (e) => console.log("結果:", e.data); </script> ``` --- ## 🔁 主執行緒 ↔ Worker 的溝通方式 | 動作 | API 說明 | | ------------ | ---------------------------- | | 發送訊息給 worker | `worker.postMessage(data)` | | 接收 worker 回覆 | `worker.onmessage = ...` | | Worker 端接收訊息 | `onmessage = function(e) {}` | | Worker 回傳主線程 | `postMessage(data)` | --- ## 📌 限制與注意事項 * Worker **無法操作 DOM** * Worker **不能存取 `window`、`document`、`alert`** * 可以使用 `fetch`、`setTimeout`、自己定義的函式等 * 與主執行緒間的資料傳遞會經過序列化(建議傳簡單資料或使用 Transferable Object) --- ## ✅ 適用情境 | 任務類型 | 適合用 Worker? | 備註 | | ------------ | ----------- | ---------------- | | 長時間 for-loop | ✅ | 可避免 UI 卡頓 | | 檔案壓縮 / 解壓縮 | ✅ | 類似 zip、json 格式處理 | | 圖片處理、濾鏡 | ✅ | 不可在主執行緒做太久 | | 畫面控制、DOM 更新 | ❌ | 不可在 Worker 執行 | --- ## 🧠 與其他技術比較 | 技術 | 執行緒 | 是否能避免 UI 卡住 | 適用於重運算? | 是否能操作 DOM | | ------------ | -------- | ---------------- | ------- | --------- | | `setTimeout` | 主執行緒 | ✅ 可稍微緩解 | ❌ 仍占用主緒 | ✅ | | `await` | 主執行緒(暫停) | ❌ 若是同步 heavy 還是卡 | ❌ | ✅ | | Web Worker | 背景執行緒 | ✅✅ | ✅✅ | ❌ | --- ## 🧩 Bonus:模組化 Worker(ES Modules 支援) ```js const worker = new Worker("worker.mjs", { type: "module" }); ``` * 支援 `import/export` 語法 * 較新瀏覽器才支援 --- ## ✅ 小結筆記 * Web Worker 是瀏覽器提供的真正背景執行環境 * 適用於**長時間、重運算邏輯** * 無法操作 DOM,僅能與主執行緒訊息傳遞 * 可以透過 `Blob` 內建 Worker,不需額外檔案 --- ## ✅ `runWorkerTask` — 通用封裝 ### ✅ 功能特性: * 可 `await` * 支援 `timeout`(防止 worker 沉默) * 支援 `error` 捕捉 * 支援一次性通信(worker 結果回來就 resolve) --- ## 📄 用法示範: ```js const worker = new Worker("worker.js"); try { const result = await runWorkerTask(worker, { action: "start" }, { timeout: 5000 }); console.log("結果:", result); } catch (err) { console.error("Worker 錯誤:", err.message); } ``` --- ## 🧠 封裝函式內容: ```js function runWorkerTask(worker, message, options = {}) { const { timeout = 10000 } = options; return new Promise((resolve, reject) => { let timeoutId; // 成功事件 const handleMessage = (e) => { clearTimeout(timeoutId); cleanup(); resolve(e.data); }; // 錯誤事件 const handleError = (e) => { clearTimeout(timeoutId); cleanup(); reject(new Error(`Worker error: ${e.message}`)); }; // 清除事件監聽器 const cleanup = () => { worker.removeEventListener("message", handleMessage); worker.removeEventListener("error", handleError); }; // 加入事件監聽器 worker.addEventListener("message", handleMessage); worker.addEventListener("error", handleError); // 設定逾時處理 timeoutId = setTimeout(() => { cleanup(); reject(new Error("Worker timeout")); }, timeout); // 發送訊息 worker.postMessage(message); }); } ``` --- ## 🔧 `worker.js`(簡單範例) ```js self.onmessage = (e) => { if (e.data.action === "start") { let sum = 0; for (let i = 0; i < 1e8; i++) sum += i; self.postMessage({ result: sum }); } }; ``` --- ## 🧠 額外細節說明 | 功能 | 說明 | | --------------------- | ---------------------------------------------- | | `removeEventListener` | 避免 memory leak,保證只對一次通信負責 | | `timeout` | 避免 worker 無限沒回應卡住 Promise | | `onerror` | 捕捉 syntax error 或 runtime error | | 傳入 `message` 格式 | 自定義,可用 `{ action: "...", payload: ... }` 模式更清晰 | --- ## 🧠 進階建議(可選) * 若要支援多次請求同一個 worker,可加 `requestId` 區分(模擬 RPC) * 若要支援「工作取消」,可擴充 abort 機制(搭配 `AbortController`) --- ## ✅ 小結:你現在擁有的封裝 * 支援 `await` * 支援逾時 * 安全清除事件監聽 * 可傳任意訊息 * 可應用於所有單次 worker request/response 場景 --- --- --- --- --- --- --- --- --- --- --- --- --- --- ### [談談 JavaScript 的 setTimeout 與 setInterval](https://kuro.tw/posts/2019/02/23/%E8%AB%87%E8%AB%87-JavaScript-%E7%9A%84-setTimeout-%E8%88%87-setInterval/) ```js $('.result-html').html(md.render($('#markdowncontent').text())); function refreshResult() { if ("Changed" === $("#response").text()) { $('.result-html').html(md.render($('#markdowncontent').val())); } window.setTimeout(refreshResult, 10000); } window.setTimeout(refreshResult, 10000); ``` ### JSDoc ```js /** * 加法函式 * @param {number} a - 第一個加數 * @param {number} b - 第二個加數 * @returns {number} 兩個數字的和 * @example * // 使用方式 * var result = add(5, 3); * console.log(result); // 輸出 8 */ function add(a, b) { return a + b; } /** * 計算數字的平方 * @param {number} num - 要計算平方的數字 * @returns {number} 平方值 * @description 這個函式接受一個數字作為參數,並返回該數字的平方值。 */ function square(num) { return num * num; } ``` ### setTimeout 該函式用於在指定的時間延遲後執行一次程式碼。 ```js setTimeout(function() { console.log("這段程式碼將在 2 秒後執行"); }, 2000); ``` ### setInterval 該函式用於以指定的間隔重複執行程式碼。 ```js // 使用 setInterval() 每隔 1 秒重複執行程式碼 let counter = 0; const intervalId = setInterval(function() { console.log("每秒執行一次,目前計數:" + counter); counter++; // 設定停止條件 if (counter === 5) { clearInterval(intervalId); // 停止執行 } }, 1000); ``` ### Base64編碼和解碼 ```js // 编碼 var originalString = "Hello, World!"; var encodedString = btoa(originalString); console.log("Encoded String: " + encodedString); // 解碼 var decodedString = atob(encodedString); console.log("Decoded String: " + decodedString); ``` ### 將 Array 中第一個項目移至另一個 Array ```js const array1 = [1, 2, 3, 4]; const array2 = []; const firstItem = array1.shift(); // 移除 array1 的第一個項目並返回該項目 array2.push(firstItem); // 將該項目添加到 array2 console.log(array1); // 輸出:[2, 3, 4] console.log(array2); // 輸出:[1] ``` ### List ```js var fruits = ["apple", "banana", "orange"]; var joinedFruits = fruits.join(", "); // 將陣列元素以逗號和空格分隔合併為一個字串 console.log(joinedFruits); // "apple, banana, orange" var numbers = [1, 2, 3, 4, 5]; var joinedNumbers = numbers.join("-"); // 將陣列元素以破折號分隔合併為一個字串 console.log(joinedNumbers); // "1-2-3-4-5" var text = "Hello"; var joinedText = Array.from(text).join(" "); // 將字串分隔合併為一個字串,以空格分隔字母 console.log(joinedText); // "H e l l o" ``` ### Map ```js // 定義一個文字對應函式的映射物件 const functionMap = { 'greet': function() { console.log('Hello!'); }, 'add': function(a, b) { return a + b; }, 'multiply': function(a, b) { return a * b; } }; // 使用映射物件執行相應的函式 functionMap['greet'](); // 印出 "Hello!" const result1 = functionMap['add'](3, 5); // result1 = 8 const result2 = functionMap['multiply'](2, 4); // result2 = 8 ``` ### Class ```js // 定義一個名為 "Person" 的類別 function Person(name, age) { // 類別的建構子,用來初始化物件的屬性 this.name = name; this.age = age; } // 在類別的原型上定義方法 Person.prototype.sayHello = function() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); }; // 在類別的原型上定義共用的屬性(可選) Person.prototype.nationality = 'Unknown'; // 創建一個 Person 物件 const person1 = new Person('Alice', 25); const person2 = new Person('Bob', 30); // 呼叫物件的方法 person1.sayHello(); // 輸出:Hello, my name is Alice and I am 25 years old. person2.sayHello(); // 輸出:Hello, my name is Bob and I am 30 years old. // 存取共用的屬性 console.log(person1.nationality); // 輸出:Unknown console.log(person2.nationality); // 輸出:Unknown ``` ```js class Person { constructor(name, age) { this.name = name; this.age = age; } sayHello() { console.log(`Hello, my name is ${this.name} and I am ${this.age} years old.`); } static staticMethod() { console.log('This is a static method.'); } } const person1 = new Person('Alice', 25); const person2 = new Person('Bob', 30); person1.sayHello(); // 輸出:Hello, my name is Alice and I am 25 years old. person2.sayHello(); // 輸出:Hello, my name is Bob and I am 30 years old. Person.staticMethod(); // 輸出:This is a static method. ``` ### MD5 ```html <script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script> ``` ```js // 要計算 MD5 的字串 const inputString = 'Hello, World!'; // 計算 MD5 雜湊 const md5Hash = CryptoJS.MD5(inputString); // 將 MD5 雜湊轉換為字串 const md5String = md5Hash.toString(); // 輸出 MD5 雜湊 console.log(md5String); ``` ### ISO 8601 Duration 時間操作 ```html <!DOCTYPE html> <html lang="zh-Hant"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>日期操作範例</title> <!-- 引入 luxon 库 --> <script src="https://moment.github.io/luxon/global/luxon.js"></script> </head> <body> <script> function manipulateDate(inputDate, duration) { // 將輸入的日期字串轉換為 DateTime 物件 const currentDate = luxon.DateTime.fromISO(inputDate); // 解析 ISO 8601 持續時間 const durationObject = luxon.Duration.fromISO(duration); // 根據持續時間調整日期 const manipulatedDate = currentDate.plus(durationObject); // 格式化結果日期物件為 "YYYY-MM-DD" 格式的字串 const resultDateStr = manipulatedDate.toISODate(); return resultDateStr; } // 示例用法 const inputDate = "2023-11-16"; const duration1 = "P2D"; const duration2 = "-PT1H"; // 循環時間表示法,表示減少一小時 const resultDate1 = manipulateDate(inputDate, duration1); const resultDate2 = manipulateDate(inputDate, duration2); console.log(resultDate1); // 輸出: 2023-11-18 console.log(resultDate2); // 輸出: 2023-11-16 </script> </body> </html> ``` ### == VS === 在 JavaScript 中,`==` 和 `===` 是用來比較兩個值是否相等的運算符,但它們之間有一些重要的差異: 1. `==` (Equality Operator): - 使用`==`進行比較時,JavaScript 會在比較之前進行類型轉換。 - 如果比較的兩個值的類型不同,JavaScript 會嘗試將它們轉換為相同的類型,然後再進行比較。 - 例如,`'5' == 5` 會返回 `true`,因為 JavaScript 會將字符串 `'5'` 轉換為數字 `5`,然後比較它們的值。 2. `===` (Strict Equality Operator): - 使用`===`進行比較時,不會進行類型轉換。 - 如果比較的兩個值的類型不同,`===` 將直接返回 `false`,即使它們的值相等。 - 例如,`'5' === 5` 會返回 `false`,因為類型不同。 簡而言之,`==` 允許類型轉換,而 `===` 不允許類型轉換,並要求值和類型都相等。 建議使用 `===` 進行比較,因為它更嚴格,不會引起類型不明確的問題,有助於減少錯誤。 ### forEach ```javascript const numbers = [1, 2, 3, 4, 5]; numbers.forEach(function (number) { console.log(number); }); numbers.forEach(number => console.log(number)); ``` ### map ```javascript const numbers = [1, 2, 3, 4, 5]; const squaredNumbers = numbers.map(function (number) { return number * number; }); // same with squaredNumbers = numbers.map(number => number * number); console.log(squaredNumbers); ``` ### onclick 帶參數 ```html <!DOCTYPE html> <html> <head> <title>Button Onclick Function with Parameter Example</title> <script> function handleButtonOnClick(param) { alert("Button clicked with parameter: " + param); } </script> </head> <body> <button onclick="handleButtonOnClick('example')">Click Me</button> </body> </html> ``` 在這個例子中,當按下按鈕時會觸發 `handleButtonOnClick` 函式,並且將字串 `example` 傳遞作為參數。當按下按鈕時,會彈出一個警告對話框,顯示出按鈕被點擊的訊息。 ### async function 在 JavaScript 中,async 函數是一種用來處理異步操作的函數。當一個函數被聲明為 async 時,該函數內部的代碼會被當做 Promise 對象執行,並且可以使用 await 關鍵字來等待異步操作的完成。 簡單來說,async 函數允許你以同步的方式編寫異步操作的程式碼,使得程式碼更易讀且更易維護。當調用 async 函數時,該函數將返回一個 Promise 對象,可以使用 then 方法來處理成功的結果,也可以使用 catch 方法來處理錯誤的情況。 以下是一個示例,展示了如何定義一個 async 函數並使用 await 關鍵字來等待異步操作的完成: ```javascript async function fetchData(url) { const response = await fetch(url); const data = await response.json(); return data; } fetchData('https://api.example.com/data') .then(data => console.log(data)) .catch(error => console.error(error)); ``` ### await 在 JavaScript 中,await 關鍵字用於等待一個 promise 物件變為 resolved 狀態(成功)或 rejected 狀態(失敗)後再繼續執行程式碼。在使用 await 時,必須在 async 函數內部使用。例如: ```javascript async function fetchData() { try { const response = await fetch('https://api.example.com/data'); const data = await response.json(); console.log(data); } catch (error) { console.error(error); } } fetchData(); ``` 在上面的例子中,fetchData 函數是一個 async 函數,其中使用了 await 來等待 fetch 和 response.json() 這兩個 promise 物件的解析結果。在最後,我們可以使用 try/catch 塊來處理可能的錯誤。 # map 在 JavaScript 中,要把一個陣列(串列)裡每個物件的兩個欄位,轉成另一種物件的兩個欄位,並集成回一個新陣列,最簡單的方法就是用 Array.prototype.map()。下面用一個完整範例來說明。 --- 範例情境 假設你有一個使用者清單,每個物件長這樣: ```js const users = [ { id: 1, name: '小明', age: 20, city: '台北' }, { id: 2, name: '小華', age: 25, city: '新北' }, { id: 3, name: '小美', age: 22, city: '台中' } ]; ``` 現在你只想要每個人 `id` 跟 `city` 這兩個欄位,並且想把它們對應到新物件裡的 `userId` 和 `location`,最後把所有新物件組成一個新陣列。 --- 作法一:用 map + 解構賦值 ```js const transformed = users.map(({ id, city }) => { return { userId: id, location: city }; }); console.log(transformed); /* [ { userId: 1, location: '台北' }, { userId: 2, location: '新北' }, { userId: 3, location: '台中' } ] */ ``` 重點說明 1. `.map()` 會對原陣列的每個元素呼叫一次傳入的函式,並把函式回傳的值收集成一個新陣列。 2. `({ id, city })` 是解構賦值,直接從 `users` 裡的物件取出 `id`、`city` 兩個欄位。 3. 回傳的新物件 `{ userId: id, location: city }` 便是我們要的格式。