了解 JavaScript 中 var
、let
、const
的使用非常重要,因為它可以確保正確的變數範圍管理,防止不必要的重新分配,並促進更清晰、更易於維護的程式碼。
變數(variable)是什麼?
變數就像魔法盒,在 JavaScript 中,我們使用變數來儲存東西。
想像一個神奇的盒子,可以容納任何東西,包括一個數字、一個單字,甚至一個不知道值得內容。
宣告變數 - var
、let
、const
(let
和 const
在 ES6 出現)var
:function scope,如果放在 {}
一樣屬於全域變數,且可以在函式的任何地方訪問,變數可被重新賦值。
function varExample() {
console.log(x); // undefined (因為 x 被提升但未初始化)
var x = 5;
console.log(x); // 5
if (true) {
var y = 10;
}
console.log(y); // 10 (var 的作用域是函式範圍,y 在 if 區塊外仍然可訪問)
}
varExample();
let
:block scope,如果放在 {}
就屬於區域變數,但在初始化之前不可訪問(暫時死區 TDZ),算是一個可隨意更改其內容的區塊區域變數,其實 let
範圍容忍性很大。
function letExample() {
console.log(x); // ReferenceError: Cannot access 'x' before initialization
let x = 5;
console.log(x); // 5
if (true) {
let y = 10;
console.log(y); // 10 (y 在 if 區塊內可訪問)
}
console.log(y); // ReferenceError: y is not defined (y 在 if 區塊外不可訪問)
}
letExample();
const
:block scope,如果放在 {}
就屬於區域變數,但在初始化之前不可訪問(暫時死區 TDZ),一個只可讀取的「不可變」常數,所以最常用在不會改變的變數,重點在「不會改變」
。
function constExample() {
console.log(x); // ReferenceError: Cannot access 'x' before initialization
const x = 5;
console.log(x); // 5
if (true) {
const y = 10;
console.log(y); // 10 (y 在 if 區塊內可訪問)
}
console.log(y); // ReferenceError: y is not defined (y 在 if 區塊外不可訪問)
x = 20; // TypeError: Assignment to constant variable (const 變數不可重新賦值)
}
constExample();
型別在程式語言中有分成靜態和動態,其中,JavaScript 是動態型別語言,會在執行中不斷轉換,也就是值的本身具備型別,值賦予到變數上時,變數的型別就會來自於值。
靜態型別:編譯時就知道變數型別
動態型別:執行時才知道變數型別
在文件中共有 7+1 種型別,主要分成兩大類:原始型別和物件型別,可以使用 typeof
看型別種類。
// 原始型別,不能擴增屬性!
console.log(typeof 'luanIsPig'); // string
console.log(typeof 1); // number
console.log(typeof true); // boolean
console.log(typeof 100n); // bigint → 就是輸入一個數字後面再加上一個 n
console.log(typeof Symbol(1)); // symbol → 原始型別,就是一個 Symbol 後面接一個數字
console.log(typeof undefined); // undefined
console.log(typeof null); // object → 這裡比較特別,是一個早期的錯誤實際上是獨立型別null
🔔 0, null, undefined 差別
圖片來源:Difference between non-zero value, 0, null & undefined in Javascript
// 物件型別可以自由擴增屬性!
console.log(typeof {}); // object
console.log(typeof []); // object
console.log(typeof function() {}); // function → 但是實際上沒有 function 型別所以歸類在 object
console.log(typeof /12345/); // object → 正規表達式也是物件
// 原始型別包裹物件就是當我們宣告原是型別時,所包裹進來的方法
let myName = ' Kuku ';
console.log(typeof myName); // string
console.log(myName.length); // 10
console.log(myName.trim()); // Kuku
console.log(myName.trim().length); // 4
JavaScript 是弱型別,很常出現型別轉來轉去的困擾;型別分為兩大類型:顯性轉換和隱性轉換,但實際上這 2 種在轉換過程沒有明確的區分,所以只能用主流大概分類說明:
顯性轉換
一眼看出是什麼型別轉換,相對單純,職場經驗中很常用到
// 數值轉換
console.log(Number('123')); // 123
console.log(Number('abc')); // NaN
console.log(parseInt('123px')); // 123
console.log(parseFloat('12.34abc')); // 12.34
// 字串轉換
console.log(String(123)); // '123'
console.log(String(true)); // 'true'
// 布林值轉換
console.log(Boolean(0)); // false
console.log(Boolean('text')); // true
隱性轉換
// 一元正負運算子「一定」會轉成數字型別,所以關鍵!一定要先判斷是否為一元運算子!
console.log(+'1'); // 1
console.log(typeof (+'1')); // number
console.log(+'50元'); // NaN
console.log(typeof (+'50元')); // number
// 前後運算元如果其中之一為 “字串” 型別,+ 視為字串運算子
console.log('5' + 2); // '52'(字串拼接)
// 前後運算元如果無法轉型為原始型別(就是指物件型別),+ 視為字串運算子
console.log(1 + {}); // 1[object Object]
console.log(1 + []); // 1[]
// 布林值可以轉成數字,而且不是字串也不是物件,+ 視為算術運算子
console.log(1 + true); // 2
寬鬆相等 (==
,筆試比較會考到):進行隱性類型
轉換,若類型不同則自動轉換後比較。
嚴格相等 (===
,開發比較會用到):嚴格比較,要求類型和值都必須相等。
console.log(1 === '1'); // false
console.log(1 == '1'); // true
// 開發曾經踩過的坑:嚴格比較例外狀況
console.log(undefined === null); // false,型別不一樣
console.log({} === {}); // false,原理在吃的記憶體位置不一樣
console.log([] === []); // false,原理在吃的記憶體位置不一樣 ⭐
console.log(new Number(0) === new Number(0)); // false,原始型別包裹物件產生新的值也是物件型別