为什么使用函数声明不适用于web工作者的onmessage



TL;DR是,当web工作者的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不会被调用。第一个<script>之后window上的fn属性的状态与web工作程序脚本开始时web工作程序中selfmessage属性的状态几乎相同:

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,侦听器将不会被附加。


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

执行!varEnvRec.SetMutableBinding(fn, fo, false)

只有当绑定不可变时(例如,如果属性不可配置(,这才会引发错误。但是onmessagegetter/setter是可配置的。

相关内容

最新更新