new MessageChannel() creates two entangled ports: whatever you postMessage() into one arrives at the other. A port is transferable, so you can hand one end to a worker, an iframe or a service worker (SW Messaging) and let the two ends talk privately afterwards. A BroadcastChannel (Broadcast Channel) shouts at every context on the origin and window.postMessage() drops everything into one shared inbox; a channel is point-to-point.
// tally-worker.js
self.onmessage = ({ ports }) => {
const port = ports[0]; // the transferred end
let total = 0;
port.onmessage = ({ data }) => port.postMessage((total += data));
};
// page: <script type="module">
const worker = new Worker('tally-worker.js');
const { port1, port2 } = new MessageChannel();
worker.postMessage('here is your end', [port2]); // transferred, not cloned
port1.onmessage = ({ data }) => console.log('running total', data);
for (const n of [5, 10, 20]) port1.postMessage(n);Output
running total 5 running total 15 running total 35
Assigning onmessage implicitly calls port.start(); with addEventListener you must call it yourself or nothing is delivered. A transferred port is detached in the sender. Baseline Widely available since Firefox 41 555 shipped it in 2015.