很多教程都在重复基础内容,比如异步函数和手写 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 结果

不需要一次性应用所有方法,根据实际需求选择合适的技巧,就能显著提升代码质量和系统性能。