`#JavaScript` `#六角前端課程` `#學習筆記` `#骨力走傱` ## 什麼是 JavaScript? ### 前端三大程式語言 * HTML:網頁結構 * CSS:網頁樣式 * JavaScript:資料互動 ### 哪裡可以運行? * 瀏覽器 * Node.js ### 如何在編輯器建立環境? 1. 建立 HTML、JS 檔。 2. 在 HTML `</body>` 結尾標籤前引入 JS 檔。 ```html= <script src="./all.js"></script> ``` 3. 切回 JS,輸入 `console.log("Hello world!!")`,查看開發者工具是否有跳出字串,若有字串,表示引入成功。 #### 為何要在 `</body>` 前引入 JS 檔? * 確保 JS 能順利操作 DOM 節點,因此等待網頁結構與樣式渲染完畢後再引入,是比較好的作法。 * [筆記|第五堂:開發思維與程式邏輯整合](/RtGsA52CTWywMNeqXKuxLg) ## 什麼是變數(Variable)與值(Value)? ### 說明 - 變數:具備名稱,儲存資料用的記憶體空間。 - 值:儲存於記憶體空間的資料。 ### 範例 | 變數 | 值 | | -------- | --- | | 香蕉數量 | 3 | | 蘋果數量 | 5 | ## 什麼是宣告變數? ### 說明 告訴程式碼開啟一個記憶體空間,並告知其空間名稱與存放的資料為何。 ```javascript= let A = "apple"; ``` - `let`:宣告的方法。 - `A`:變數。 - `=`:賦予。 - `"apple"`:值。 - `;`:中斷程式碼。 > `"apple"` 賦予在名稱為 `A` 的變數上。 > (若要翻譯成自然語言,通常會從右往左讀。) ## 宣告的種類 ### let ```javascript= let milkPrice = 10; ``` ```javascript= milkPrice = 20; console.log(milkPrice); // 20 ``` * 不可重複宣告相同的變數名稱。 * 可重新賦值。 * 宣告後,可以不賦值。 ### const ```javascript= const sunNum = 1; ``` ```javascript= sunNum = 2; Uncaught TypeError: Assignment to constant variable. // 無法修改常數 ``` * 又稱常數、唯讀變數。 * 不可重複宣告相同的變數名稱。 * 不可重新賦值。 * 宣告後,一定要賦值。 * 不常更動或不希望輕易被修改的值,通常會使用 `const` 宣告。 ### var ```javascript= var carColor = "red"; ``` ```javascript= carColor = "blue"; console.log(carColor); // blue ``` * 可重複宣告相同的變數名稱。 * 可重新賦值。 * 宣告後,可以不賦值。 * 較不嚴謹,故容易汙染到其他資料,不推薦使用。 ### 比較 | | let | const | var | | ---------- | --- | ----- | --- | | 重複宣告 | ❌ | ❌ | ✔️ | | 重新賦值 | ✔️ | ❌ | ✔️ | | 可以不賦值 | ✔️ | ❌ | ✔️ | ## 變數命名 ### 命名規則 1. 不可使用數字開頭。 2. 有區分大、小寫(`catNum`、`catnum` 為不同的記憶體空間)。 3. 可使用中文命名。 4. 可使用特殊符號開頭(`_`、`$`)。 5. 不可使用[保留字(關鍵字)](https://www.w3schools.com/Js/js_reserved.asp)命名。 ### 命名習慣 #### 駝峰式大小寫 當變數名稱和函式名稱是多個單字連結在一起時,利用「駝峰式大小寫」來表示,以增加變數和函式的可讀性。 | | 小駝峰 | 大駝峰 | | ---- | -------------------------------------------------- | ---------------------------------------------------------- | | 說明 | 第一個單字以小寫字母開始;第二個單字的首字母大寫。 | 每一個單字的首字母都採用大寫字母,也被稱為 Pascal 命名法。 | | 範例 | `firstName`、`lastName` | `FirstName`、`LastName` | #### 語意化 注意變數名稱是否語意化,以利於後續維護與團隊協作。 * u ❓ * usr ❓ * userName ✔️ #### 名詞?動詞? * 變數通常會使用名詞命名,並且注意單、複數。 * 布林值或函式命名時,會使用 `has`、`is` 等動詞命名。 ## 原始型別介紹 ### 七種原始型別 1. Boolean 2. Null 3. Undefined 4. Number 5. BigInt 6. String 7. Symbol > 原始型別又稱基本型別,另外還有物件型別。 ### typeof:查詢當前資料的型別 ```javascript= let phoneNuber = "03123123"; typeof phoneNuber; // string ``` * `phoneNuber` 儲存的資料為電話號碼,並非一組用來運算的數字,故使用字串型別儲存。 * `typeof` 會返回當前值的資料型別。 ## 原始型別:Boolean ### 介紹 ```javascript= let a = 1; let b = 2; let c = a > b; console.log(c); // false typeof c; // boolean ``` * Boolean(布林),只有 `true` 與 `false` 兩個值。 * 程式判斷邏輯時,會使用到布林值。 ## 原始型別:Null ### 介紹 ```javascript= let a = 1000; console.log(a); // 1000 a = null; console.log(a); // null typeof a; // null ``` * Null(空值),**開發者手動賦予值**,但其值沒有存放任何資料。 * 型別為 object(根據 w3c 的說明,可以先將件事視為一個 bug)。 * 可用於清除資料使用。 ## 原始型別:Undefined ### 介紹 ```javascript= let a; console.log(a); // Undefined typeof a; // Undefined ``` * Undefined(未定義),**程式自動判斷的型別**,表示宣告了一個變數,但未賦予值。 * 此情形程式並不會爆錯,可繼續往後執行。 * 若是出現 `not defined`,表示未宣告變數,程式會報錯。 ## 原始型別:Number ### 介紹 ```javascript= let milkPrice = 10; ``` * Number(數字),Javascript 的數值型別。 * 整數、帶有小數點的浮點數字、`Infinity`(無限大)、`-Infinity`(負無限大),以及 `NaN`(Not a Number)都屬於這一類。 ### 什麼是賦值運算子? * 這個就叫賦值運算子 → `=`。 * 其功能是將「右側運算元的值」**賦予**給「左側運算元」;也就是, `x = y` 會把 `y` 的值賦予給 `x`。 | 名稱 | 簡化的運算子 | 意義 | | -------- | ------------ | ----------- | | 賦值 | `x = y` | `x = y` | | 加法賦值 | `x += y` | `x = x + y` | | 減法賦值 | `x -= y` | `x = x - y` | | 除法賦值 | `x /= y` | `x = x / y` | | 餘數賦值 | `x %= y` | `x = x % y` | ### 什麼是 `a++` 與 `++a`? 都是用來將變數的值增加 1 的運算符,稱之為**遞增運算符**。 | | a++ | ++a | | ---- | -------- | ---- | | 名稱 | 後置遞增 | 前置遞增 | | 意義 | 先返回變數的當前值,然後再將變數加 1。 | 先將變數加 1,然後返回變數的新值。 | ### a++ ```javascript= let a = 5; let b = a++; console.log(a); // 6 console.log(b); // 5 ``` * `let b = a++;` 時,`a` 先將當前的值(5)賦予給 `b`,接著 `a` 的值增加 1。 * 因此 `a` 的最終值為 6,`b` 的值則是 5 。 ### ++a ```javascript= let a = 5; let b = ++a; console.log(a); // 6 console.log(b); // 6 ``` * `let b = ++a;` 時,`a` 的值增加 1(變成 6)之後,再將值賦予給 `b`。 * 因此 `a`、`b` 的最終值都是 6。 ### 資料來源 * [初學者指南:JavaScript 中的 a++ 和 ++a 有什麼不同?](https://realnewbie.com/coding/javascript/javascript-post-increment-pre-increment/) ### 什麼是 NaN? ```javascript= let a = 5; let b = "banana"; let c = a * b; console.log(c); // NaN typeof c; // number ``` * NaN(Not a Number),不是數字,是 JavaScript 的特殊數值。 * 在某些無法運算的情況下,例如數字型別與文字型別**相乘**時,便會出現 **NaN**。 * NaN 的型別為數字。 ## 原始型別:BigInt ### 介紹 ```javascript= const b = BigInt("1234567890123456789012345678901234567899"); console.log(b); // 1234567890123456789012345678901234567899n typeof b; // bigint ``` * 在 JavaScript 中,`Number` 使用的是 64 位浮點數,表示能處理的數字範圍是有限的,能處理的數字範圍為 `-(2^53 - 1)` 到 `2^53 - 1`,也就是 -9007199254740991 到 9007199254740991。 * 在 ES2020 引入新的型別 `BigInt` 解決此問題。 * 可以表示非常大的整數,而不失精準度。 * 可以進行運算,但不能與 `Number` 混合運算(兩者為不同型別!)。 * `BigInt` 的除法會向下取整。 ### 如何使用 BigInt? #### 在數字後方加入 `n` ```javascript= const b = 1234567890123456789012345678901234567899n; console.log(b); // 1234567890123456789012345678901234567899n typeof b; // bigint ``` #### `BigInt()` ```javascript= const b = BigInt("1234567890123456789012345678901234567899"); console.log(b); // 1234567890123456789012345678901234567899n typeof b; // bigint ``` ### 資料來源 * [BigInt 的基礎與應用](https://ithelp.ithome.com.tw/articles/10365264) ## 原始型別:String ### 介紹 ```javascript= let apple = "蘋果"; ``` * String(字串),表示字元的集合,一個字元代表一個字母。 * 需要用成對的 `"` 或 `'`,包住字串,否則程式碼會視為變數,造成預期外的狀況。 ### 字串與數字相加 ```javascript= let apple = "蘋果"; let num = 10; let res = apple + num; typeof res; // string ``` * 字串型別可與數字型別相加,這個過程中發生了**自動轉型**,將數字型別轉型成文字型別,故 `typeof` 的結果會是字串。 ### 樣版字面值 #### 使用前 ```javascript= let myName = "Josh"; let hobby = "看電影"; let content = "您好,我是" + myName + ",興趣是" + hobby; console.log(content); // 您好,我是Josh,興趣是看電影 ``` #### 使用後 ```javascript= let myName = "Josh"; let hobby = "看電影"; let content = `您好,我是 ${myName},興趣是${hobby}`; console.log(content); // 您好,我是 Josh,興趣是看電影 ``` * 使成對的反引號包住字串,並用 `${}` 引入變數資料。 ## 原始型別:Symbol ### 介紹 ```javascript= let appleOne = Symbol("apple"); let appleTwo = Symbol("apple"); console.log(appleOne === appleTwo); // false // typeof appleOne; // Symbol // typeof appleTwo; // Symbol ``` * ES6 引入的新型別,用來創建獨一無二的識別值。 * 即使兩個 Symbol 內容相同,但也會被電腦視為不同的值。 * 可作為物件的屬性名稱,常用於避免屬性名稱衝突的情況。 ### 如何使用 Symbol? #### `Symbol()` ```javascript= const sym1 = Symbol(); const sym2 = Symbol('desc'); const sym3 = Symbol('desc'); console.log(sym2 === sym3); // false console.log(sym2.description); // desc ``` * 每個 Symbol 創建的值都是唯一的,故 sym2 與 sym3 不相等。 * 使用 `description` 取得描述內容。 ### 實際應用 #### 未使用 Symbol ```javascript= let nameList = { "John" : { yearsOld : 10 }, "John" : { yearsOld : 9 } } console.log(nameList); // John : { yearsOld : 9 } ``` * 物件中出現相同的屬性名稱時,後方的屬性名稱會蓋掉前方的,因此最後只剩下 `John : { yearsOld : 9 }` 這個值。 #### 使用 Symbol ```javascript= let nameList = { [Symbol("John")] : { yearsOld : 10 }, [Symbol("John")] : { yearsOld : 9 } } console.log(nameList);  // Symbol(John) : {yearsOld: 10} // Symbol(John) : {yearsOld: 9} ``` * 在物件中,使用 `Symbol()` 建立屬性名稱,能確保每一個屬性名稱都是唯一的,因此後方的 John 不會蓋掉前方的 John。 * 使用 `[]` 包住 `Symbol()` 建立的屬性名稱,否則會被視為字串。 ### 資料來源 * [資料型別 Symbol 使用時機](https://ithelp.ithome.com.tw/articles/10220499) ## 自動轉型與強制轉型 {%preview https://hackmd.io/@SorryFish/SkZEQv9Cle %} ## 淺談陣列與物件 ### 什麼是陣列?什麼是物件? 陣列與物件是一種資料集合,能幫助開發者更有效率的管理與設計資料。 ### 範例 > 情境:紀錄水果行的資料。 #### 未使用陣列與物件 ```javascript= const shopName = "fruitShop"; let appleNum = 10; let bananaNum = 20; let boxNum = 100; ``` #### 使用陣列與物件 ```javascript= const shopDate = { "shopName" : "fruitShop", "fruits" : ["apple","banana"], "boxNum" : 100 } ``` 在資料不多的情況下,也許能單獨將資料儲存於變數上,但絕大多數的形況: 1. 資料數龐大。 2. 需依照開發需求,設計資料結構。 3. 需考量後續的維護與可讀性。 因此需要使用陣列與物件的概念儲存資料。 ## 陣列 ### 宣告陣列 ```javascript= let arr = []; let data = ["abc", 10, false]; let fruits = ["apple", "banana"]; let fruitsNum = [10, 5]; let number = [1, 2, 3, 4, [23, 24]]; ``` * 一個清單,由元素和索引構成。 * 使用 `[]` 包住資料,並用 `,` 隔開每一筆資料(最後一筆不用 `,`)。 * 陣列內可以是空值、任何型別的資料,甚至是陣列或物件。 ### 讀取與儲存陣列資料 ```javascript= let fruits = ["apple", "banana"]; console.log(fruits[0]); // "apple" let hotItem = fruits[0]; console.log(hotItem); // "apple" ``` * `變數 [ 索引值 ] ;` * 有些程式語言,基於記憶體位置的「偏移量」,會從 0 開始計算,因此在上述的範例中,若要取出 `"apple"`,索引值要輸入 0 。 * `let hotItem = fruits[0];` 將 `fruits[0]` 的結果,賦予在另一個變數上,就能單獨使用其資料。 ### 資料來源 * [為什麼索引值從 0 開始算?](https://kaochenlong.com/2024/03/03/why-array-index-begin-with-zero.html) * [物件與陣列設計](https://hackmd.io/bA80b0sqSPe2wX6vGGC2VA?view) ### 讀取與儲存陣列長度 ```javascript= let fruits = ["apple", "banana"]; console.log(fruits.length); // 2 let fruitsNum = fruits.length; console.log(fruitsNum); // 2 ``` * `變數.length`:返回陣列中有幾筆資料。 ### 寫入資料 #### 按照索引位置寫入 ```javascript= let fruits = []; fruits[0] = "apple"; fruits[1] = "banana"; fruits[2] = "melon"; console.log(fruits); // ['apple', 'banana', 'melon'] ``` * `變數 [ 索引值 ] = 值;` #### 不照索引位置寫入 ```javascript= let fruits = []; fruits[0] = "apple"; fruits[1] = "banana"; fruits[2] = "melon"; fruits[5] = "lemon"; console.log(fruits); // ['apple', 'banana', 'melon', 空白 × 2, 'lemon'] ``` * 未賦予值的索引位置為空值。 * `length` 的結果會是 6,空值也是值。 #### `push` ```javascript= let fruits = ["apple", "banana"]; fruits.push("lemon"); console.log(fruits); // ['apple', 'banana', 'lemon'] ``` * `變數.push ( 要寫入的值 );` * `push` 為 JavaScript 提供的方法,可將資料寫入至陣列**末端**。 ## 物件 ### 宣告物件 ```javascript= let band = { genres : ["progressive rock", "indie rock", "post-punk"], instruments : ["vocals", "guitar", "bass", "drum"], membersNum : 3, isActive : true }; ``` * 一個物體,由鍵與值構成與描述。 * `let 變數 = { 屬性 : 值 };` * 屬性=key;值=value。 * 物件內可以是空值、任何型別的資料,甚至是陣列或物件。 ### 讀取與儲存物件資料 ```javascript= const band = { genres : ["progressive rock", "indie rock", "post-punk"], instruments : ["vocals", "guitar", "bass", "drum"], membersNum : 3, isActive : true }; console.log(band.genres); // ['progressive rock', 'indie rock', 'post-punk'] const bandGenres = band.genres; console.log(bandGenres); // ['progressive rock', 'indie rock', 'post-punk'] ``` * `變數.屬性`:返回儲存的值。 * `let bandGenres = band.genres;` 將 `band.genres` 的結果,賦予在另一個變數上,就能單獨使用其資料。 ### 寫入資料 ```javascript= const band = {}; band.genres = ["progressive rock", "indie rock", "post-punk"]; band.instruments = ["vocals", "guitar", "bass", "drum"]; band.membersNum = 3; band.isActive = true; console.log(band); //genres : ["progressive rock", "indie rock", "post-punk"] //instruments : ["vocals", "guitar", "bass", "drum"] //membersNum : 3 //isActive : true ``` * `變數.屬性 = { 要寫入的值 };` ## 原始型別傳值 vs 物件型別傳址 ### 原始型別傳值 * 將值複製一份,放到新的記憶體位置給新的變數使用。 * 新、舊變數的記憶體位置不同,因此後續有任何變更,都不會互相影響。 ```JavaScript let a = 1; let b = a; b = 2; console.log(a, b); // 1, 2 ``` | 變數 | 記憶體位置 | 值 | | ---- | ---------- | ----------------------------------- | | a | 0x01 | 1 | | b | 0x02 | ~~1(a 的值)~~ → 2(重新賦值到 b) | ### 物件型別傳址 * 傳值又稱傳參考。 * 將變數參考的記憶體位置,傳到新的變數上。 * 此時新、舊變數參考的記憶體位置是同一個,因此後續有任何變更,都會互相影響。 ```javascript= let arrA = [1, 2, 3]; let arrB = a; arrB.push(50); console.log(arrA, arrB); // [1, 2, 3, 50] [1, 2, 3, 50] ``` | 變數 | 記憶體位置 | 值 | | ---- | ---------- | ---------------------------------------------------- | | arrA | 0x03 | ~~[1, 2, 3]~~ **[1, 2, 3, 50]**(跟著變更) | | arrB | 0x04 | ~~0x03([1, 2, 3])~~ **[1, 2, 3, 50]**(`push` 50) | ### 為何可以修改 const 宣告的物件? ```javascript= const user = { name: "John", age: 20 }; // 允許修改屬性。 user.age = 21; user.name = "Wick"; // 不允許變數重新賦值,這代表指向的記憶體位置會被修改。 user = { name: "New User" }; ``` * 嚴格來說,可以修改的是 const 宣告的物件「內容」。 * const 限制的是變數指向的**記憶體位置**,而該位置中的物件內容(屬性)是可以修改的。