先记住这个答案
Worker 的全局对象是 DedicatedWorkerGlobalScope(共享 Worker 是 SharedWorkerGlobalScope),通过 self 引用,没有 window。可用:fetch、XMLHttpRequest、WebSocket、IndexedDB、setTimeout、Promise、importScripts、postMessage、部分加密与存储 API。不可用:DOM、document、window、alert 等依赖渲染与用户界面的接口。与主线程只能通过结构化克隆的消息通信,无法直接共享普通对象。
- Worker 无 window 和 DOM
- 网络与存储 API 大多可用
- 通信靠结构化克隆消息
为什么 Worker 有独立全局对象而非共享 window
Worker 运行在独立线程,浏览器为其创建 DedicatedWorkerGlobalScope 作为全局对象,self 指向它而非 window。这样设计的根本原因是 DOM 与渲染管线不是线程安全的:如果允许后台线程直接读写 document,主线程的布局、绘制会与 Worker 的修改产生竞态,浏览器的渲染假设将全部失效。
因此规范把全局对象切成两类能力:与线程无关的通用能力(网络、存储、定时器、加密、Worker 嵌套)被保留在 Worker 作用域;与窗口、文档、用户交互绑定的能力(DOM、alert、requestAnimationFrame、History 的部分操作)只存在于主线程的 window 上。判断一个 API 是否可用,先问它是否触碰渲染或 UI。
在 Worker 中解析大文件并写入 IndexedDB
场景:页面接收用户上传的 200MB CSV,要求解析、聚合后离线缓存,且界面不卡顿。决策:创建专用 Worker,在主线程用 FileReader 或流读取后以分片 postMessage 传入;Worker 内做解析,通过 indexedDB 直接写入结果,只把聚合摘要发回主线程渲染。
为什么这样处理:解析是纯计算,IndexedDB 在 Worker 可用,两者都不需要 DOM;只有最终展示需要主线程。约束是不能在 Worker 里直接更新进度条 DOM,必须定期 postMessage 进度数值,由主线程更新界面。结果:解析期间主线程保持响应,数据落地也无需跨线程搬运整份结果。
function aggregate(chunks) {
const sums = new Map();
let done = 0;
const progress = [];
for (const chunk of chunks) {
for (const row of chunk) {
sums.set(row.k, (sums.get(row.k) || 0) + row.v);
}
done++;
progress.push(Math.round((done / chunks.length) * 100));
}
return { progress, total: [...sums.values()].reduce((a, b) => a + b, 0) };
}
const chunks = [
[{ k: 'a', v: 3 }, { k: 'b', v: 5 }],
[{ k: 'a', v: 2 }],
[{ k: 'b', v: 1 }, { k: 'c', v: 4 }]
];
const r = aggregate(chunks);
console.log(JSON.stringify(r));查看输出与解释
{"progress":[33,67,100],"total":15}演示分片聚合与按分片回报进度的逻辑;真实 Worker 中 progress 应通过 postMessage 发给主线程更新 DOM。
哪些判断容易失效:XHR 的 responseXML 与 API 子集差异
边界一:同名 API 在 Worker 里可能是阉割版。XMLHttpRequest 可用,但 responseXML 恒为 null,因为解析 XML 成文档需要 DOM;依赖它解析响应的旧代码会在 Worker 静默失效,需要改用 responseText 加 DOMParser 之外的方式或回主线程处理。
边界二:新 API 的支持度因浏览器与 Worker 类型而异,例如 OffscreenCanvas 可在 Worker 中离屏渲染,支持 2D、WebGL 等上下文,也可由主线程创建后 transfer 控制权,或在 Worker 内直接创建。对应处理是能力检测:在 Worker 内用 typeof OffscreenCanvas !== 'undefined' 之类的探测决定路径,代价是要维护两套实现或优雅降级。
容易答错的地方
- 以为 self 等价于 window
- 在 Worker 中
self指向DedicatedWorkerGlobalScope,不包含 DOM 相关属性;把主线程代码原样搬进 Worker,访问window或document会直接抛 ReferenceError。 - 以为 Worker 完全不能联网或存数据
- 规范明确保留
fetch、XMLHttpRequest、WebSocket与 IndexedDB;真正被禁的是渲染与 UI 相关接口,按是否触碰 DOM 判断即可,而非一概否定。
面试官还会怎么问?
Worker 能用 localStorage 吗?
不能。localStorage 是同步 API 且与文档源绑定,只挂在 window 上;Worker 中应使用 IndexedDB 或 Cache API 做持久化存储。
Worker 里弹提示或确认框怎么办?
alert、confirm 在 Worker 不可用,因为模态 UI 只能由主线程驱动。正确做法是 Worker postMessage 请求,主线程弹出 UI 后把用户选择再发回 Worker。
如何快速确认某 API 在 Worker 是否可用?
查 MDN 的 Worker 可用函数清单,或在 Worker 内做能力检测,如 typeof indexedDB !== 'undefined';比依赖浏览器版本号判断更可靠。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。