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 {
return await 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) => {
return Promise.all(
promises.map(
p =>
new Promise((resolve, reject) => {
signal.addEventListener("abort", () =>
reject(new domException("Cancelled", "AbortError"))
);
p.then(resolve).catch(reject);
})
)
);
};
// 使用示例
const controller = new AbortController();
setTimeout(() => controller.abort(), 2000);
await createCancellablePool(
[analyticssync(), cacheHydration()],
controller.signal
);这在用户切换页面时非常有用,可以取消不需要的异步任务,节省资源。
4. 使用异步生成器处理大量数据
一次性加载大量数据可能导致内存问题。使用异步生成器可以按需处理数据。
async function* streamResults(urls) {
for (const url of urls) {
const response = await fetch(url);
yield response.json();
}
}
// 使用示例
const videoStream = streamResults(videoUrls);
for await (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 = new Uint8Array(total);
let offset = 0;
buffers.forEach(buffer => {
result.set(new Uint8Array(buffer), offset);
offset += buffer.byteLength;
});
return result.buffer;
};适用于 WebAssembly、WebGL 或 WebSocket 等场景。
6. 使用 Error cause 链接错误信息
在复杂的异步操作中,错误信息可能不够详细。使用 Error cause 可以保留原始错误信息。
async function processOrder() {
try {
await validatePayment();
} catch (err) {
throw new Error("Payment failed", { cause: err });
}
}
try {
await processOrder();
} catch (e) {
console.error("Root cause:", e.cause);
}这样可以在日志中看到完整的错误链,便于排查问题。
7. 安全枚举对象属性
直接使用 for...in 遍历对象可能意外访问到原型链上的属性。使用属性描述符可以避免这个问题。
const getSafeKeys = (obj) => {
return Object.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 池可以限制并发数量。
class PromisePool {
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 上获取状态信息。
function trackPromise(promise) {
const state = {
status: "pending",
value: null
};
const proxy = new Proxy(promise, {
get(target, prop) {
if (prop === "status") return state.status;
if (prop === "value") return state.value;
return Reflect.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 可以在对象被垃圾回收时自动清理缓存。
class TemporaryCache {
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) {
const ref = this.cache.get(key);
return ref?.deref();
}
}
// 使用示例
const cache = new TemporaryCache();
cache.set("user:123", heavyUserObject);当缓存的对象不再被使用时,缓存项会自动删除,适合大对象的短期缓存。
总结
这些技巧展示了 Js 从脚本语言到系统语言的演进:
使用 Performance API 获取精确时间
使用 AbortController 取消异步任务
使用 WeakRef 管理内存
使用 Error cause 追踪错误来源
使用 Promise 池控制并发
使用 TypedArray 处理二进制数据
安全枚举对象属性
使用 Proxy 观察 Promise 状态
使用异步生成器处理大量数据
使用 void 明确丢弃 Promise 结果
不需要一次性应用所有方法,根据实际需求选择合适的技巧,就能显著提升代码质量和系统性能。
本文内容仅供个人学习/研究/参考使用,不构成任何决策建议或专业指导。分享/转载时请标明原文来源,同时请勿将内容用于商业售卖、虚假宣传等非学习用途哦~感谢您的理解与支持!