JS實戰技巧:提升代碼質量與性能的十個方法

很多教程都在重復基礎內容,比如異步函數和手寫 Promise。如果你已經熟悉這些知識,下面這十個高級技巧可以幫助你更深入地理解 JavaScript 。這些方法來自實際項目經驗,能夠解決真實問題,比如內存泄漏、性能瓶頸和資源管理。
這些技巧曾經在實際項目中帶來顯著改進:
- 減少線上服務內存泄漏 38%
- 降低數據庫成本 60%
- 提升批處理速度 3.2 倍
無論是不被處理的異步操作,還是容易出問題的連接池,這些方法都能幫助你構建更穩定的系統。
1. 使用 void 處理立即執行的異步函數
立即執行的異步函數可能會返回一個未被處理的 Promise,導致內存泄漏或未捕獲的錯誤。使用 void 可以明確表示我們不關心這個 Promise 的結果。
// 不推薦:返回的 Promise 沒有被處理
(async () => {
await initializeApp();
})();
// 推薦:使用 void 明確丟棄返回值
void (async () => {
await initializeApp();
})();這種方法適用于啟動時的異步任務,比如加載配置或初始化緩存。使用 void 可以讓代碼意圖更清晰,避免工具提示未處理的 Promise。
2. 使用 Performance api 精確測量時間
console.time 適合粗略測量,但如果需要更精確的時間數據,可以使用 Performance API。
const measureAsync = async (name, fn) => {
performance.mark(`${name}-start`);
try {
returnawait fn();
} finally {
performance.mark(`${name}-end`);
performance.measure(name, `${name}-start`, `${name}-end`);
const [entry] = performance.getEntriesByName(name);
console.log(`?? ${name}: ${entry.duration.toFixed(3)}ms`);
performance.clearMarks();
}
};
// 使用示例
await measureAsync("DatabaseTransaction", () =>
db.transaction(complexQuery)
);這種方法可以在瀏覽器的性能面板中查看詳細數據,適合測量數據庫操作或外部接口調用。
3. 使用 AbortController 取消異步任務
AbortController 不僅可以用于 fetch 請求,還可以取消任何異步任務。
const createCancellablePool = (promises, signal) => {
returnPromise.all(
promises.map(
p =>
newPromise((resolve, reject) => {
signal.addEventListener("abort", () =>
reject(newdomException("Cancelled", "AbortError"))
);
p.then(resolve).catch(reject);
})
)
);
};
// 使用示例
const controller = new AbortController();
setTimeout(() => controller.abort(), 2000);
await createCancellablePool(
[analyticssync(), cacheHydration()],
controller.signal
);這在用戶切換頁面時非常有用,可以取消不需要的異步任務,節省資源。
4. 使用異步生成器處理大量數據
一次性加載大量數據可能導致內存問題。使用異步生成器可以按需處理數據。
asyncfunction* streamResults(urls) {
for (const url of urls) {
const response = await fetch(url);
yield response.json();
}
}
// 使用示例
const videoStream = streamResults(videoUrls);
forawait (const video of videoStream) {
if (shouldStopProcessing(video)) break;
renderPreview(video);
}這種方法適合處理大量數據,比如日志文件或視頻元信息,內存占用更穩定。
5. 使用 TypedArray 處理二進制數據
處理二進制數據時,TypedArray 比普通數組更高效。
const mergeBuffers = (buffers) => {
const total = buffers.reduce((sum, b) => sum + b.byteLength, 0);
const result = newUint8Array(total);
let offset = 0;
buffers.forEach(buffer => {
result.set(newUint8Array(buffer), offset);
offset += buffer.byteLength;
});
return result.buffer;
};適用于 WebAssembly、WebGL 或 WebSocket 等場景。
6. 使用 Error cause 鏈接錯誤信息
在復雜的異步操作中,錯誤信息可能不夠詳細。使用 Error cause 可以保留原始錯誤信息。
asyncfunctionprocessOrder() {
try {
await validatePayment();
} catch (err) {
thrownewError("Payment failed", { cause: err });
}
}
try {
await processOrder();
} catch (e) {
console.error("Root cause:", e.cause);
}這樣可以在日志中看到完整的錯誤鏈,便于排查問題。
7. 安全枚舉對象屬性
直接使用 for...in 遍歷對象可能意外訪問到原型鏈上的屬性。使用屬性描述符可以避免這個問題。
const getSafeKeys = (obj) => {
returnObject.entries(Object.getOwnPropertyDescriptors(obj))
.filter(([_, desc]) => desc.enumerable)
.map(([key]) => key);
};
// 使用示例
const safeDict = Object.create(null);
safeDict.data = "test";
console.log(getSafeKeys(safeDict)); // ["data"]這在處理外部數據時特別重要,可以避免原型污染。
8. 使用 Promise 池控制并發數量
一次性發送大量請求可能壓垮服務。使用 Promise 池可以限制并發數量。
classPromisePool{
constructor(concurrency) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
async run(task) {
return new Promise((resolve, reject) => {
const execute = async () => {
this.running++;
try {
resolve(await task());
} catch (err) {
reject(err);
} finally {
this.running--;
this.next();
}
};
this.queue.push(execute);
this.next();
});
}
next() {
while (this.queue.length && this.running < this.concurrency) {
this.queue.shift()();
}
}
}
// 使用示例
const pool = new PromisePool(3);
await pool.run(() => generateReport());這可以保護數據庫或第三方服務不被過多請求壓垮。
9. 使用 Proxy 觀察 Promise 狀態
在 UI 中,我們經常需要顯示異步操作的狀態。使用 Proxy 可以直接在 Promise 上獲取狀態信息。
functiontrackPromise(promise) {
const state = {
status: "pending",
value: null
};
const proxy = newProxy(promise, {
get(target, prop) {
if (prop === "status") return state.status;
if (prop === "value") return state.value;
returnReflect.get(target, prop);
}
});
promise
.then(result => {
state.status = "fulfilled";
state.value = result;
})
.catch(() => {
state.status = "rejected";
});
return proxy;
}
// 使用示例
const dataPromise = trackPromise(fetch("/api/data"));這樣不需要額外維護狀態變量,可以直接從 Promise 獲取狀態。
10. 使用 WeakRef 實現自動清理的緩存
緩存是常見的優化手段,但容易導致內存泄漏。使用 WeakRef 可以在對象被垃圾回收時自動清理緩存。
classTemporaryCache {
constructor() {
this.cache = new Map();
this.cleanup = new FinalizationRegistry((key) => {
this.cache.delete(key);
});
}
set(key, value) {
this.cache.set(key, new WeakRef(value));
this.cleanup.register(value, key, value);
}
get(key) {
constref = this.cache.get(key);
returnref?.deref();
}
}
// 使用示例
const cache = new TemporaryCache();
cache.set("user:123", heavyUserObject);當緩存的對象不再被使用時,緩存項會自動刪除,適合大對象的短期緩存。
總結
這些技巧展示了 Js 從腳本語言到系統語言的演進:
- 使用 Performance API 獲取精確時間
- 使用 AbortController 取消異步任務
- 使用 WeakRef 管理內存
- 使用 Error cause 追蹤錯誤來源
- 使用 Promise 池控制并發
- 使用 TypedArray 處理二進制數據
- 安全枚舉對象屬性
- 使用 Proxy 觀察 Promise 狀態
- 使用異步生成器處理大量數據
- 使用 void 明確丟棄 Promise 結果
不需要一次性應用所有方法,根據實際需求選擇合適的技巧,就能顯著提升代碼質量和系統性能。


































