先记住这个答案
在专用 Worker 中,全局对象是 DedicatedWorkerGlobalScope,可通过 self 引用。它没有 window,也不支持 DOM 操作,因此不能直接访问页面元素。替代的是 postMessage() 将数据回传父线程,close() 主动终止自身。与 window 相比,postMessage 固定发往创建它的父线程,而 close 会清空事件循环中的剩余任务。理解 self 是 Worker 内全局对象的代名词,有助于区分运行时可用 API。
- DedicatedWorkerGlobalScope 是专用 Worker 的全局对象
- self 是全局引用,提供 postMessage、close 等
- close 会丢弃后续任务,主线程 terminate 是外部强制
专用 Worker 全局对象的构成与 postMessage 单方向性
DedicatedWorkerGlobalScope 从 WorkerGlobalScope 继承,并通过 self 暴露。它没有 window、document 等与页面渲染相关的接口,因此不能触碰 DOM。创建 Worker 时传入的 name 可通过 self.name 读取,这对于多 Worker 场景下区分身份有帮助。
它的 postMessage 方法只向创建它的父线程发送消息,与主线程的 worker.postMessage 形成单向通道。同时它有 close() 方法,调用后立即关闭,且丢弃事件循环中尚未执行的任务。相对于 window 的 self,它更纯粹地专注于计算与数据交换,没有导航、存储等方法。
在 Worker 内实现自终止的检测任务
考虑一个数据分析 Worker:它接收一批文件路径,逐个解析。当中途遇到无法恢复的错误且重试达到 3 次时,Worker 内部调用 self.close() 终止自己。相比主线程事后 worker.terminate(),这种自终止能让 Worker 在执行完当前同步清理后立即结束,主线程无需记录复杂状态。
实现上,Worker 在 onmessage 处理循环中检查错误计数,一旦达到阈值就调用 close()。此时后续不会再有消息处理,但已通过 postMessage 发出的结果仍能到达主线程。这种设计将生命周期决策下沉到任务内部,适合任务可自我判定终点的场景。
容易误判的边界:API 可用性与 close 后行为
有的开发者会以为 Worker 里有 window,但实际没有。任何依赖 window、document、localStorage 的代码都会报错。虽然 requestAnimationFrame 和 fetch 在专用 Worker 中可用,但并非所有 DOM 相关 API 都被实现,应单独检测或使用替代方案。
close() 的即时性容易被忽略:调用会丢弃事件循环中排队的任务,但不会取消已发起的异步操作(如 fetch 或定时器),其回调也不会再执行。若希望优雅退出,应在 close 前完成所有异步收尾,否则可能丢失结果。
容易答错的地方
- 误以为 self 是 window 的别名
- 在专用 Worker 中 self 指向 DedicatedWorkerGlobalScope,没有任何 window 属性,使用 window 会抛 ReferenceError。可通过在 Worker 内 console.log(typeof window) 验证为 undefined。
- 认为 postMessage 可以指定目标
- DedicatedWorkerGlobalScope.postMessage 只将消息发送给创建它的父线程,没有目标参数。若需与其他线程通信,必须通过 MessageChannel 或 BroadcastChannel 等额外机制。
面试官还会怎么问?
Worker 内部调用 close() 后,主线程的 worker.terminate() 还有必要吗?
如果 Worker 已自发调用 close() 且当前代码执行完毕,它会终止,此时无需再调用 terminate()。但 close() 不会中断当前正在执行的同步代码,若调用 close() 后 Worker 仍处于长循环等同步任务中,主线程的 terminate() 可以强制中断。因此唯有需要立即停止正在运行的代码时才有必要。
DedicatedWorkerGlobalScope 有没有 onmessage 属性?
有。它继承并支持 message 事件,可通过 self.onmessage 或 addEventListener('message') 监听消息。两者都可用,但避免同时混用导致多次处理。
为什么在专用 Worker 中访问 location 或 navigator 可以?
因为 WorkerGlobalScope 提供了 location 和 navigator 的只读版本,它们暴露 referrer、userAgent 等有限信息,但没有页面导航和跳转能力,且受同源策略限制。fetch 是全局函数,不属于 location 或 navigator。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。