---
# System prepended metadata

title: 【JavaScript 筆記】語法（資料型別、變數） - part 2
tags: [Web, HTML, 網頁程式設計, JavaScript, CSS, 網頁]

---

# 【JavaScript 筆記】語法（資料型別、變數） - part 2

[TOC]

歡迎你點入本篇文章，本系列網頁程式設計，主要紀錄我個人自學的軌跡，另外也作為日後個人複習用。若你喜歡本篇文章，歡迎在文章底下點一顆愛心，或是追蹤我的個人公開頁～

---

## 資料型別（Data Type）

![image](https://hackmd.io/_uploads/B1i0-laoZl.png)

Image Source：https://www.geeksforgeeks.org/javascript/variables-datatypes-javascript/

JavaScript 中所有的值都屬於某種資料型別，型別分為兩大類：

1. Primitive（原始型別）的值儲存的是值本身。
2. Non-Primitive（非原始型別）儲存的是記憶體位址的參考（Reference）。

另外，JS 的語法也定義了兩個型別的值：

1. Literals（實字）：一個固定值，就是常數的意思。
2. Variables（變數）：隨時會變動的值。

### 用 `typeof` 查看型別

`typeof` 運算子可在 Console 中直接查看任何值的型別：

![image](https://hackmd.io/_uploads/SJjClxajWe.png)

### 原始型別（Primitive）

#### 1. Number（數字）

JS 的 Number 型別同時涵蓋整數與浮點數，底層都是 64 位元雙精度浮點數（IEEE 754 標準），安全整數範圍是 $\pm (2^{53} - 1)$ 。

範例：

```javascript=
let age    = 21          // 整數
let price  = 99.9        // 浮點數
let neg    = -50         // 負數

// 特殊數值
let inf    = Infinity    // 無限大 (例如 1/0)
let negInf = -Infinity   // 負無限大
let notNum = NaN         // Not a Number (例如 "abc" * 2)

console.log(0.1 + 0.2)  // 0.30000000000000004 (浮點數精度問題)
console.log(typeof NaN)   // "number"
```

`NaN` 是非數字，但 `typeof NaN` 卻回傳 `"number"`，這是 JS 著名的怪癖之一。

#### 2. BigInt（大整數）

Number 型別無法精確表示超過 $2^{53} - 1$ 的整數，此時就要用 BigInt，在數字後面加上 n 即可宣告。

![image](https://hackmd.io/_uploads/BktGNbTibl.png)

從上圖測試結果可看到：

- `9007199254740991 + 2` 的結果會是 `9007199254740992`，應該要是 `9007199254740993` 才對，這代表已經超過 `Number` 的整數上限了。
- 也可看到 BigInt 輸出的結果會最後帶有一個 n：`9007199254740993n`。
- `console.log(10n + 5)` 代表 BigInt 跟 Number 是不能相加的。

#### 3. String（字串）

用來表示文字，可以用單引號、雙引號或反引號包起來。

範例：

```javascript=
let s1 = 'Hello'           // 單引號
let s2 = "World"           // 雙引號
let s3 = `哈囉，${s1}！`   // 反引號 (樣板字串，可嵌入變數)

console.log(s3)            // 哈囉，Hello！
console.log(typeof s1)     // "string"

// 常用字串操作
let str = "JavaScript"
console.log(str.length)          // 10 (字串長度)
console.log(str.toUpperCase())   // "JAVASCRIPT"
console.log(str.slice(0, 4))     // "Java" (擷取第0到第3個字)
console.log(str.includes("Script")) // true (是否包含某字串)
```

實際執行畫面：

![image](https://hackmd.io/_uploads/Sy4xH-6s-g.png)

#### 4. Boolean（布林值）

只有兩個可能的值：`true`（真）或 `false`（假），是所有判斷式與流程控制的核心。

範例：

```javascript=
let isLoggedIn = true
let isAdmin    = false

console.log(typeof true)  // "boolean"

if (isLoggedIn) {
  console.log("歡迎回來！")
} else {
  console.log("請先登入")
}

// 比較運算會產生 Boolean
console.log(10 > 5)   // true
console.log(10 === 5) // false
```

實際執行畫面：

![image](https://hackmd.io/_uploads/r1n8Sb6s-l.png)

Falsy 值：以下這些值在布林判斷中都被視為 false：

```javascript=
Boolean(0)          // false
Boolean("")         // false
Boolean(null)       // false
Boolean(undefined)  // false
Boolean(NaN)        // false
Boolean(false)      // false
```

反之，以上六個以外的所有值，都是 truthy（被視為 true）。

#### 5. Null（空值）

null 在 JS 裡面是一種原始值，表示沒有值。

範例：

```javascript=
let user = null

console.log(user)         // null
console.log(typeof null)  // "object" 

// 要正確判斷 null，必須用嚴格相等
console.log(user === null)  // true
```

`typeof null` 回傳 `"object"` 是 JS 的歷史遺留 bug，實際上 `null` 是獨立的原始型別，不是物件。

#### 6. Undefined（未定義）

`undefined` 代表變數被宣告了，但還沒被賦值，是 JS 自動給予的預設值 。

範例：

```javascript=
let x             // 宣告但未賦值
console.log(x)    // undefined

function greet(name) {
  console.log(name)
}
greet()           // undefined（沒有傳入參數）

console.log(typeof undefined)  // "undefined"
```

#### Null vs Undefined

|        | null        | undefined   |
| ------ | ----------- | ----------- |
| 意思     | 刻意設為空     | 還沒被賦值       |
| 誰設定的   | 程式設計師    | JS 引擎       |
| 使用情境   | 主動清空一個變數    | 尚未初始化       |
| typeof | `"object"` | `"undefined"` |

#### 7. Symbol（符號）

Symbol 是 ES6 新增的型別，用來建立全域唯一的識別字，即使描述相同的兩個 Symbol 也不相等。

範例：

```javascript=
let id1 = Symbol("id")
let id2 = Symbol("id")

console.log(id1 === id2)   // false, 這邊永遠都不相等
console.log(typeof id1)    // "symbol"

// 主要用途: 當作物件的唯一屬性 key，避免命名衝突
let uniqueKey = Symbol("secretKey")
let obj = {
  name: "LukeTseng",
  [uniqueKey]: "這是隱藏屬性"
}

console.log(obj[uniqueKey])  // "這是隱藏屬性"
console.log(obj.name)        // "LukeTseng"
```

Symbol 主要出現在設計框架、函式庫或需要嚴格避免 key 碰撞的場景。

### 非原始型別（Non-Primitive）

非原始型別又稱參考型別（Reference Type），變數儲存的不是值本身，而是指向記憶體中資料的位址。

#### 1. Object（物件）

物件是用來儲存一組相關資料與功能的容器，以鍵值對（key-value pair）的形式組織 。

```javascript=
// 建立物件
let student = {
  name:   "LukeTseng",         // 字串
  age:    19,             // 數字
  isActive: true,         // 布林
  greet: function() {     // 函式（稱為方法 Method）
    console.log(`我是 ${this.name}`)
  }
}

// 存取屬性（兩種方式）
console.log(student.name)       // "LukeTseng"（點記法）
console.log(student["age"])     // 21（括號記法）
student.greet()                 // 我是 LukeTseng

// 新增 / 修改 / 刪除屬性
student.city = "高雄"    // 新增
student.age  = 22        // 修改
delete student.isActive  // 刪除

console.log(typeof student)  // "object"
```

#### 2. Arrays（陣列）

陣列是有順序的資料集合，用中括號 `[]` 表示，每個元素用索引（Index）存取，從 0 開始計算。

```javascript=
// 建立陣列
let fruits = ["Apple", "Banana", "Mango"]
let scores = [95, 87, 72, 100]
let mixed  = ["Luke", 21, true, null]  // 可以混合不同型別

// 存取元素
console.log(fruits[0])     // "Apple"
console.log(fruits[2])     // "Mango"
console.log(fruits.length) // 3（陣列長度）

// 常用陣列方法
fruits.push("Grape")         // 在末尾新增
fruits.pop()               // 刪除末尾元素
fruits.unshift("Strawberry")      // 在開頭新增
fruits.shift()             // 刪除開頭元素

console.log(fruits.includes("Banana"))  // true
console.log(fruits.indexOf("Mango"))   // 2

console.log(typeof fruits)  // "object", 陣列的 typeof 也是 object
Array.isArray(fruits)       // true, 要判斷是否為陣列，用這個方法
```

#### 3. Function（函式）

函式是可以重複執行的程式碼區塊，可以被存進變數、當參數傳遞。

```javascript=
// 函式宣告
function add(a, b) {
  return a + b
}
console.log(add(3, 5))  // 8

// 函式運算式（Expression）
let multiply = function(a, b) {
  return a * b
}
console.log(multiply(3, 5))  // 15

// 箭頭函式（Arrow Function, ES6 新增, 最常用）
let divide = (a, b) => a / b
console.log(divide(10, 2))   // 5

// 函式可以存入陣列、物件，也可以當參數傳入另一個函式（稱為 Callback）
let greet = (name) => `哈囉，${name}！`
let names = ["LukeTseng", "Amy", "Bob"]
names.forEach(name => console.log(greet(name)))
// 哈囉，LukeTseng！
// 哈囉，Amy！
// 哈囉，Bob！

console.log(typeof greet)  // "function"
```

### 原始型別（Primitive） vs 非原始型別（Non-Primitive）

```javascript=
// 原始型別: 複製值, 互不影響
let a = 10
let b = a
b = 99
console.log(a)  // 10 (a 不受 b 影響)

// 非原始型別: 複製參考位址, 會互相影響
let obj1 = { name: "Luke" }
let obj2 = obj1          // obj2 指向同一個記憶體位址
obj2.name = "Amy"
console.log(obj1.name)   // "Amy" (obj1 也被改了)
```

| 特性   | Primitive      | Non-Primitive |
| ---- | -------------- | ------------- |
| 儲存的是 | 值本身            | 記憶體位址（參考）     |
| 複製後  | 各自獨立           | 共用同一份資料       |
| 比較方式 | 比較值            | 比較記憶體位址       |
| 可變性  | 不可變（Immutable） | 可變（Mutable）   |

## 變數（Variables）

變數（Variable）是用來替記憶體中某塊空間取名字的，方便用於儲存與重複使用資料。

在 JS 中，宣告變數有三種關鍵字：`var`、`let`、`const` 。

### 1. `var`（舊式，不推薦）

var 是 ES6（2015）以前唯一的宣告方式，但它有幾個令人困惑的特性，現代開發幾乎已棄用。

語法：

```javascript
var 變數名稱 = 值
```

#### 特性一：函式作用域（Function Scope）

`var` 的有效範圍是整個函式或全域，而不是 `{}` 區塊，表示在 `if`、`for` 等區塊內宣告的 `var`，外面也看得到：

```javascript=
if (true) {
  var msg = "hi"
}
console.log(msg)  // "hi"
```

用我們一般學過程式語言的知識來說，這樣存取應該是存取不到的，但卻可以。

#### 特性二：Hoisting（提升）

JS 引擎在執行前，會先把 `var` 的宣告移到最頂端（但不帶值），導致使用時機早於宣告卻不報錯，只會得到 `undefined`：

```javascript=
console.log(x)  // undefined（沒有報錯, 但還沒有值）
var x = 10
console.log(x)  // 10
```

實際上 JS 把上面的程式碼理解成：

```javascript=
var x          // ← 宣告被提升到頂端
console.log(x) // → undefined
x = 10
```

實際的執行畫面：

![image](https://hackmd.io/_uploads/B1zrXzasWe.png)

#### 特性三：可重複宣告

```javascript=
var name = "LukeTseng"
var name = "Amy"
console.log(name)  // "Amy"
```

實際的執行畫面：

![image](https://hackmd.io/_uploads/r1IuXGpo-l.png)

### 2. `let`（推薦）

ES6 推出的 `let` 解決了 `var` 的大部分問題，是現在最常用的宣告方式。

語法：

```javascript
let 變數名稱 = 值
```

#### 特性一：區塊作用域（Block Scope）

有效範圍是 `{}` 包住的區塊，出了區塊就無法存取：

```javascript=
if (true) {
  let msg = "hi"
  console.log(msg)
}
console.log(msg)    // ReferenceError: msg is not defined
```

實際執行畫面：

![image](https://hackmd.io/_uploads/SyhA7fpoWx.png)

#### 特性二：有 Hoisting 但不初始化（TDZ）

`let` 也會被 Hoisting，但不會初始化為 `undefined`，在宣告前使用會直接報錯。

這段宣告前的禁區稱為 `Temporal Dead Zone`（TDZ，暫時性死區）：

```javascript=
console.log(y)  // ReferenceError: Cannot access 'y' before initialization
let y = 20
```

實際執行畫面：

![image](https://hackmd.io/_uploads/SkHo4z6oWg.png)

#### 特性三：不能重複宣告，但可以重新賦值

```javascript=
let score = 80
// let score = 90  // SyntaxError: 不能重複宣告
score = 90          // 可以重新賦值
console.log(score)  // 90
```

### 3. `const`（宣告常數）

`const` 用來宣告不會再被重新賦值的變數，宣告時必須立刻給值。

語法：

```javascript
const 變數名稱 = 值
```

範例：

```javascript=
const PI = 3.14159
// PI = 3  // TypeError: Assignment to constant variable.
// const AGE  // SyntaxError: 必須立刻初始化

console.log(PI)  // 3.14159
```

`const` 阻止的是重新賦值，不是值的內容被修改，如果 `const` 存的是物件或陣列，其內部的屬性仍然可以修改：

```javascript=
const user = { name: "Luke", age: 19 }

// user = {}          // 不能重新賦值（整個替換）
user.age = 22         // 可以修改物件內部的屬性
user.city = "高雄"    // 可以新增屬性
console.log(user)     // { name: "Luke", age: 22, city: "高雄" }

const nums = [1, 2, 3]
nums.push(4)          // 可以修改陣列內容
console.log(nums)     // [1, 2, 3, 4]
```

實際執行畫面：

![image](https://hackmd.io/_uploads/Hy4SUfTobe.png)

### `var` / `let` / `const` 比較

| 特性       | var               | let         | const       |
| -------- | ----------------- | ----------- | ----------- |
| 作用域      | 函式作用域或全域             | 區塊作用域       | 區塊作用域       |
| Hoisting | 初始化為 undefined | TDZ，提早存取報錯 | TDZ，提早存取報錯 |
| 重複宣告     | ✓可以              | ✗不行        | ✗不行        |
| 重新賦值     | ✓可以              | ✓可以        | ✗不行        |
| 宣告時需給值   | 不需要             | 不需要       | 必須        |
| 現代推薦程度   | 不推薦             | 優先使用      | 優先使用      |

### 變數名稱規則（識別字）

識別字（Identifier）就是幫變數、函式取的名字，然後 JS 對於變數名稱有以下硬性規則：

- 只能包含：英文字母（a-z、A-Z）、數字（0-9）、底線（`_`）、錢字號（`$`）。
- 不能以數字開頭。
- 大小寫有差異（`name` 和 `Name` 是不同的變數）。
- 不能是關鍵字（如 `let`、`const`、`if`、`return` 等）。

以下是合法與非法的變數識別字範例：

```javascript=
// 合法
let userName = "LukeTseng"
let _private = 42
let $price   = 99
let score2   = 100

// 非法
// let 2score = 100    // 數字開頭
// let user-name = ""  // 含有連字號
// let my name = ""    // 含有空格
// let var = 5         // 保留字
```

命名慣例（非強制，但業界共識）：

```javascript=
// 變數、函式 → camelCase（小駝峰）
let firstName = "LukeTseng"
let totalScore = 100
function getUserName() {}

// 類別 → PascalCase（大駝峰）
class UserProfile {}

// 常數（固定不變的值）→ 全大寫 + 底線
const MAX_SIZE = 100
const API_KEY  = "abc123"
```

### 一行多變數宣告

```javascript=
// 宣告多個變數, 同時賦值
let name = "Luke", age = 21, city = "高雄"

// 宣告多個但只部分賦值
let x, y, z
x = 10
y = 20
// z 此時為 undefined

// const 也可以，但每個都必須立刻賦值
const PI = 3.14, E = 2.71, PHI = 1.618

let firstName = "Luke",
    lastName  = "Tseng",
    score     = 95

console.log(`${firstName} ${lastName}，分數：${score}`)
// Luke Tseng，分數：95
```

### JS 是弱型別的程式語言

一些編譯式語言，如 C、C++ 等，都是強型別的程式語言，在宣告變數時一定要指定資料型別，如 C++ 寫法：

```cpp=
int age = 21;
string name = "LukeTseng";
```

JavaScript 完全不需要，因為它是弱型別（Weakly Typed）+動態型別（Dynamically Typed）的語言：

- 動態型別：變數的型別在執行時自動判斷，不需事先宣告。
- 弱型別：不同型別的值可以自動轉換互相運算（隱式型別轉換）。

```javascript=
// 同一個變數，型別可以隨時改變
let x = 42          // Number
console.log(typeof x)  // "number"

x = "Hello"         // 改成 String，完全合法
console.log(typeof x)  // "string"

x = true            // 改成 Boolean
console.log(typeof x)  // "boolean"

// 弱型別: 不同型別自動轉換
console.log("5" + 3)    // "53"（數字被轉成字串，做字串拼接）
console.log("5" - 3)    // 2  （字串被轉成數字，做減法）
console.log(true + 1)   // 2  （true 被轉成 1）
console.log(false + 1)  // 1  （false 被轉成 0）
```

## 總整理

JavaScript 中的值分為兩大類別，並且可以透過 `typeof` 運算子來檢查任何值的型別：

- Primitive（原始型別）：變數直接儲存值本身，複製時各自獨立，互不影響（不可變）。
- Non-Primitive（非原始型別 / 參考型別）：變數儲存的是記憶體位址，複製時會共用同一份資料，牽一髮動全身（可變）。

另外本篇提到的其他兩個名詞：

- Literals（實字）：程式碼中寫死的固定值（常數）。
- Variables（變數）：用來儲存值、隨時可變動的容器。

### 原始型別（Primitive Types）

- Number（數字）：
    - 涵蓋整數與浮點數（底層皆為 64 位元浮點數）。
    - 安全整數範圍是 $\pm (2^{53} - 1)$。
    - 包含特殊值 `Infinity` 與 `NaN`。
    - 注意：`typeof NaN` 的結果為 `"number"`。
- BigInt（大整數）：專門處理超過 $2^{53} - 1$ 的巨大整數，宣告時需在數字後方加上 n（例如 10n）。
    - 無法直接與 Number 型別混合運算。
- String（字串）：文字型別，使用單引號、雙引號或反引號包覆。
    - 反引號（樣板字串）支援 `${}` 嵌入變數。
- Boolean（布林值）：僅有 `true` 與 `false`，是流程控制的核心。
    - 在 JS 中會被判定為 `false` 的 Falsy 值只有六個：
        1. `0`
        2. `""`（空字串）
        3. `null`
        4. `undefined`
        5. `NaN`
        6. `false`
- Null（空值）：開發者刻意設定的空值。
    - 注意：`typeof null` 回傳 `"object"` 是 JS 的歷史遺留 Bug。
- Undefined（未定義）：變數已宣告但尚未賦值時，JS 引擎自動給予的預設狀態。
- Symbol（符號）：ES6 新增，用來建立全域唯一的識別字，最常用於物件的唯一屬性 Key，避免命名衝突。

### 非原始型別（Non-Primitive Types）

- Object（物件）：以鍵值對（key-value pair）組合而成的資料容器。
    - 可透過點記法（`obj.key`）或括號記法（`obj["key"]`）存取與修改屬性。
- Array（陣列）：有順序的資料集合，使用 `[]` 宣告，索引值從 0 開始。
    - `typeof` 檢查陣列會回傳 `"object"`，需改用 `Array.isArray()` 來精確判斷。
- Function（函式）：可重複執行的程式碼區塊。
    - 分為函式宣告、運算式與 ES6 箭頭函式（`=>`）。

### 變數（Variables）宣告

JavaScript 提供三種宣告變數的方式，現代開發強烈建議棄用 `var`，優先使用 `let` 與 `const`。

| 特性       | var               | let         | const       |
| -------- | ----------------- | ----------- | ----------- |
| 作用域      | 函式作用域或全域             | 區塊作用域       | 區塊作用域       |
| Hoisting | 初始化為 undefined | TDZ，提早存取報錯 | TDZ，提早存取報錯 |
| 重複宣告     | ✓可以              | ✗不行        | ✗不行        |
| 重新賦值     | ✓可以              | ✓可以        | ✗不行        |
| 宣告時需給值   | 不需要             | 不需要       | 必須        |
| 現代推薦程度   | 不推薦             | 優先使用      | 優先使用      |

關於 `const` 的重要觀念：`const` 拒絕的是重新賦值的動作，若 `const` 儲存的是物件或陣列（參考型別），其內部的屬性或元素依然可以被新增、修改或刪除。

### 變數命名規則與 JS 語言特性

- 命名規則：只能包含大小寫英文字母、數字、底線（`_`）與錢字號（`$`），不能以數字開頭、區分大小寫，且不可使用系統保留關鍵字。
- 業界命名慣例：
    - 變數與函式使用小駝峰（camelCase）
    - 類別使用大駝峰（PascalCase）
    - 常數使用全大寫加底線（MAX_SIZE）。
- 動態弱型別特性：JavaScript 不需事先宣告資料型別（動態型別），且不同型別的值互相運算時，會自動進行隱式型別轉換（弱型別），例如 `"5" + 3` 會變成字串 `"53"`，而 `"5" - 3` 則會算出數字 `2`。

## 參考資料

[Variables and Datatypes in JavaScript - GeeksforGeeks](https://www.geeksforgeeks.org/javascript/variables-datatypes-javascript/)

[Data types](https://javascript.info/types)

[Rules for naming variables in JavaScript - DEV Community](https://dev.to/imashwani/rules-for-naming-variables-in-javascript-g54)

[Mastering JavaScript Naming Conventions: 10 Best Practices for Cleaner Code | Syncfusion Blogs](https://www.syncfusion.com/blogs/post/top-10-javascript-naming-convention)