为什么使用函数声明对 web worker 的 onmessage 不起作用?

Why does using a function declaration not work for onmessage with web workers?

TL;DR 是当网络工作者的 onmessage() 处理程序被定义为函数声明而不是函数表达式时,它不起作用。 (至少在 Chrome。)我想不出为什么。

我有以下非常简单的示例设置:

index.html

<!DOCTYPE html>
<html>
<head>
  <title>Web worker test</title>
  <script src="index.js"></script>
</head>
<body>
  <input type="button" value="test1" onclick="run('test1.js')">
  <input type="button" value="test2" onclick="run('test2.js')">
</body>
</html>

index.js

function run(filename) {
  worker = new Worker(filename);
  worker.postMessage("test");
}

test1.js

onmessage = function(msg) {
    console.log(msg.data);
}
console.log(self.onmessage);

test2.js

function onmessage(msg) {
    console.log(msg.data);
}
console.log(self.onmessage);

您可以在 http://mrbean.cryogenia.com/~davedude/webworkertest/ 上自己尝试。如果您单击 test1 按钮,它会记录以下内容(至少在 Chrome 79.0.3945.117 上):

ƒ (msg) {
  console.log(msg.data);
}
test1.js:2 test

这表明函数已按预期定义,处理程序正在运行。

相比之下,当你点击test2时,你得到的是:

ƒ onmessage(msg) {
  console.log(msg.data);
}

这表明脚本 正在 工作,并且函数已定义,只是出于某种原因无法作为处理程序运行。

全局对象的onmessage属性是一个setter。 Setter 不会被函数声明调用;相反,它们只是被覆盖,就好像 delete <fnName> 之前是 运行 一样。这是另一个例子:

<script>
Object.defineProperty(window, 'fn', {
  set(newVal) {
    console.log('setter invoked');
  },
  configurable: true
});
</script>
<script>
function fn() {
}

console.log(window.fn);
</script>

如您所见,setter 没有被调用。 fn 属性 在第一个 <script> 之后 window 的状态几乎与 message 属性 在 [=] 的状态相同19=] 在网络工作者脚本开始的网络工作者中:

const workerFn = () => {
  console.log(Object.getOwnPropertyDescriptor(self, 'onmessage'));
};
const workerFnStr = `(${workerFn})();`;
const blob = new Blob([workerFnStr], { type: 'text/javascript' });
const worker = new Worker(window.URL.createObjectURL(blob));
<div>Look at results in your browser console, not the snippet console</div>

<br><br>

<img src="https://i.stack.imgur.com/8JND8.png">

将函数设置为 message 事件侦听器的功能只会在 setter 内部发生。但是如果不调用 setter,则不会附加侦听器。


如果你很好奇,in the specification,当顶层的函数声明存在,并且该名称已经存在于全局对象中时,该名称将不会被重新创建或重新初始化,直到, 最后:

Perform ! varEnvRec.SetMutableBinding(fn, fo, false).

这将引发错误 only if the binding is immutable(例如,如果 属性 不可配置)。但是 onmessage getter/setter 可配置的。