JavaScript 實戰模式
觀察者模式(Event Emitter)
class EventEmitter {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
return this;
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event]
.filter(cb => cb !== callback);
}
return this;
}
emit(event, ...args) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(...args));
}
return this;
}
}
// 使用
let bus = new EventEmitter();
bus.on("userLogin", (user) => console.log(`${user.name} 登入了`));
bus.on("userLogin", (user) => updateUI(user));
bus.emit("userLogin", { name: "小明" });
狀態機(State Machine)
class StateMachine {
constructor(initialState, transitions) {
this.state = initialState;
this.transitions = transitions;
}
transition(action) {
let key = `${this.state}_${action}`;
let nextState = this.transitions[key];
if (nextState) {
console.log(`${this.state} → ${nextState}`);
this.state = nextState;
return true;
}
console.warn(`無效轉換:${this.state} + ${action}`);
return false;
}
}
// 訂單狀態機
let orderMachine = new StateMachine("pending", {
"pending_pay": "paid",
"paid_ship": "shipped",
"shipped_deliver": "delivered",
"pending_cancel": "cancelled",
"paid_refund": "refunded"
});
orderMachine.transition("pay"); // pending → paid
orderMachine.transition("ship"); // paid → shipped
orderMachine.transition("deliver"); // shipped → delivered
orderMachine.transition("refund"); // 無效轉換
防抖與節流(完整版)
// 防抖:連續觸發只執行最後一次
function debounce(fn, delay, immediate = false) {
let timer;
return function(...args) {
let callNow = immediate && !timer;
clearTimeout(timer);
timer = setTimeout(() => {
timer = null;
if (!immediate) fn.apply(this, args);
}, delay);
if (callNow) fn.apply(this, args);
};
}
// 節流:固定間隔執行一次
function throttle(fn, limit) {
let lastCall = 0;
return function(...args) {
let now = Date.now();
if (now - lastCall >= limit) {
lastCall = now;
fn.apply(this, args);
}
};
}
深拷貝
// 簡單版(不支持函式、Date、RegExp 等)
function deepClone(obj) {
return JSON.parse(JSON.stringify(obj));
}
// 現代版(推薦)
let clone = structuredClone(originalObj);
// 完整版(支持所有類型)
function deepCloneComplete(obj, seen = new WeakMap()) {
if (obj === null || typeof obj !== "object") return obj;
if (seen.has(obj)) return seen.get(obj);
if (obj instanceof Date) return new Date(obj);
if (obj instanceof RegExp) return new RegExp(obj);
let clone = Array.isArray(obj) ? [] : {};
seen.set(obj, clone);
for (let key of Object.keys(obj)) {
clone[key] = deepCloneComplete(obj[key], seen);
}
return clone;
}
請求佇列
// 控制並行請求數量
class RequestQueue {
constructor(maxConcurrent = 3) {
this.max = maxConcurrent;
this.running = 0;
this.queue = [];
}
async add(requestFn) {
return new Promise((resolve, reject) => {
this.queue.push(async () => {
try {
resolve(await requestFn());
} catch (err) {
reject(err);
} finally {
this.running--;
this.processNext();
}
});
this.processNext();
});
}
processNext() {
while (this.running < this.max && this.queue.length > 0) {
this.running++;
this.queue.shift()();
}
}
}
// 使用:最多同時 3 個請求
let queue = new RequestQueue(3);
let urls = ["/api/1", "/api/2", "/api/3", "/api/4", "/api/5"];
let results = await Promise.all(
urls.map(url => queue.add(() => fetch(url).then(r => r.json())))
);