才疏学浅,以前没听过structured clone(结构化克隆)这个词语,最近才看到。
Channel Messaging API ,允许附着在同一个文档上的两个运行在不同上下文的脚本直接通信。
window下的postMessage
postMessage这个东西以前也用过,比如下面这个demo所示的用法:
我用的webstorm
index.html嵌套一个iframe,在index.html中的js代码如下:
window.addEventListener('message',function(e){
console.log('message,',e.data)
},false);
在iframe中的js如下:
window.parent.postMessage('来自iframe的呼唤', 'http://localhost:63342/');
index.html的控制台会输出:
message, 来自iframe的呼唤
web worker与postMessage
demo的逻辑是父页面传递给worker一个数据,worker修改之后在返回。
父页面:
let worker = new Worker('worker.js');
let obj = [[0], [1]];
worker.postMessage(obj);
worker.addEventListener('message', function (data) {
console.log('传递给worker的数据为', JSON.stringify(obj));
console.log('从worker传过来的数据为:', JSON.stringify(data.data));
});
worker.js:
self.addEventListener('message', function (data) {
console.log('从父页面传过来的数据:', JSON.stringify(data.data));
data.data[2] = [2];
console.log('传递给父页面的数据为:', JSON.stringify(data.data));
postMessage(data.data);
});
跑一下,输出为:
从父页面传过来的数据: [[0],[1]]
传递给父页面的数据为: [[0],[1],[2]]
传递给worker的数据为 [[0],[1]]
从worker传过来的数据为: [[0],[1],[2]]
奇诡了吧,父页面传递给worker的是一个包含数组的数组,并且在worker中做了修改,但是父页面中的obj居然没发生变化。这个就是结构化克隆。
The structured clone algorithm is an algorithm defined by the HTML5 specification for copying complex JavaScript objects. It is used internally when transferring data to and from Workers via postMessage() or when storing objects with IndexedDB.
用这个东西就能实现深度克隆了吧!
MessageChannel实现深度克隆
在页面中实现深度克隆的时候总不能专门开个worker吧。
这是第一次接触MessageChannel。
MessageChannel 有两个只读的属性,port1和port2。
The port1 …… originated the channel….
The port2 …… the other end of the channel, which the message is initially sent to.
我试了一下,两个可以互换。
深度克隆,实现如下:
function structuralClone(obj) {
return new Promise(resolve => {
const {port1, port2} = new MessageChannel();//对象的解构赋值
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);//port.postMessage(message, transferList);,第二个参数可选
});
}
const obj = [1, [2, 3]];
structuralClone(obj).then(e => {
obj[1] = [4, 5];
console.log(`原数组修改过后:`, JSON.stringify(obj));
console.log(`复制后的数组:`, JSON.stringify(e));
});
上面的代码基本是照抄的参考文献3,但是有修改,原本的报错,因为await
这个深度克隆不可以克隆Error对象, Function对象, symbols, DOM节点。
好几个postMessage,有点乱了,整理一下他们的参数,表格如下:
postMessage | 第一个参数 | 第二个参数 | 第三个参数 |
---|---|---|---|
port.postMessage | message | transferList ,可选 | 无 |
worker.postMessage | message | transferList,可选 | 无 |
targetWindow.postMessage | message | targetOrigin | transfer,可选 |
参考文献:
1. https://developer.mozilla.org/en-US/docs/Web/API/Channel_Messaging_API
2. https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
3. https://dassur.ma/things/deep-copy/
4. https://developer.mozilla.org/en-US/docs/Web/API/Worker
5. https://juejin.im/entry/5adfe5476fb9a07a9d6ffe16