程式要寫得穩、不會莫名出錯,需要靠紮實的語法與邏輯觀念。之前只大致講一些JS觀念,這幾天剛好複習,常見語法、作用域、函式、閉包、除錯/風格等等,讓我們可以寫出更「可靠」的 JS
JavaScript 裡有兩種基本單位:
* // statement
let x = 10; // 宣告是個 statement
if (x > 5) { // if 是 statement
console.log('大於 5');
}
// expression
let y = (2 + 3) * 4; // (2+3) 是 expression,會有結果 5
小提醒:分號(;)不是強制,但養成加分號的習慣能避免某些自動插入分號(ASI)造成的奇怪 bug
const PI = 3.1415; // 常數(不會被改)
let username = "Hank"; // 可重賦值
常見型別:Number, String, Boolean, null, undefined, Object, Array, Function
把常做的事包成函式,別重複寫!
function add(a, b) {
return a + b;
}
const add = function(a, b) { return a + b; };
const add = (a, b) => a + b; // 當體只有一個 expression 時可以省略 return 與大括號
function greet(name = '訪客') { console.log('哈囉 ' + name); }
function sum(...nums) { return nums.reduce((s, n) => s + n, 0); }
為什麼用函式?
決定變數在哪裡可見:
Global scope(全域) → 在任何地方都能存取(小心污染)
Function scope(函式作用域) → let/const 在函式內有效
Block scope(區塊作用域) → let/const 在 {} 裡有效(var 沒有)
function demo() {
let a = 1; // 只在 demo 裡可見
if (true) {
let b = 2; // 只在這個區塊可見
}
// console.log(b); // 會錯
}
實務建議:盡量用 const → let,避免 var。
函式宣告(function foo(){})會被提升,可以在宣告之前呼叫。
變數宣告(var) 也會被提升但值為 undefined,容易造成困惑。
let / const 也會被提升到區塊頂端,但在宣告前讀取會拋錯(TDZ),所以不要靠提升。
console.log(foo()); // 可以
function foo(){ return 'hi'; }
console.log(x); // undefined(若 x 用 var)
var x = 3;
console.log(y); // ReferenceError(若 y 用 let)
let y = 4;
閉包是函式與其詞法環境的組合。最常見用法:製作私有變數或累加器
function makeCounter() {
let count = 0;
return function() {
count++;
return count;
};
}
const c = makeCounter();
console.log(c()); // 1
console.log(c()); // 2 (內部的 count 被「記住」)
實務用途:事件處理保有狀態、製作工廠函式、避免全域變數
用 ===(嚴格比較)取代 ==(型別會被自動轉換,易出錯)
Falsy 值:0, '', null, undefined, NaN, false。
其餘為 truthy
0 == '0' // true
0 === '0' // false -> 建議用 ===
try {
JSON.parse('不合法json');
} catch (e) {
console.error('解析失敗', e);
}
寫一個 greet(name),回傳「哈囉,NAME」
用閉包做一個 makeCounter(),回傳 increment 函式
寫 sumAll(...nums),回傳總和
(答案放在下方,先試試看)
小練習解答
1.
greet(name)
function greet(name) {
return `哈囉,${name}`;
}
console.log(greet("xxx"));
// 輸出:哈囉,xxx
✔ 這裡用 模板字串 (template string) 會比字串相加更直覺。
✔ 也可以寫成 return "哈囉," + name;。
function makeCounter() {
let count = 0; // 私有變數,外面存取不到
function increment() {
count++;
return count;
}
return increment;
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
✔ 這裡的重點在 閉包 (closure):
count 雖然定義在 makeCounter 裡,但因為 increment 還在用它,所以 JS 不會回收。
每次呼叫 counter(),都在操作同一個 count~
function sumAll(...nums) {
return nums.reduce((acc, cur) => acc + cur, 0);
}
console.log(sumAll(1, 2, 3, 4)); // 10
console.log(sumAll(5, 10)); // 15
✔ 這裡用 ...nums 把不定數量的參數收集起來變成陣列。
✔ 再用 reduce 做加總,簡潔好讀。
✔ 也可以用迴圈寫:
let total = 0;
for (let n of nums) total += n;
return total;
掌握函式、作用域與閉包後,可以發現很多「看起來神奇」的網頁互動其實都是由這些文法構成。比起只靠模仿範例,理解這些語法會讓你寫程式更有把握、錯誤更好找,也更容易學進階的 ES6/模組與框架