Fundamentals 13 min read

Exploring New ECMAScript Proposals: Discard Bindings, Iterator Chunking, and More

This article reviews several Stage 2 ECMAScript proposals—including discard bindings using the void operator, iterator chunking for sliding windows and non‑overlapping sequences, phase‑based ESM imports for static worker initialization, extractors for custom destructuring, and structs with shared memory, mutexes, and unsafe blocks.

Taobao Frontend Technology
Taobao Frontend Technology
Taobao Frontend Technology
Exploring New ECMAScript Proposals: Discard Bindings, Iterator Chunking, and More

Discard (void) Bindings

Proposal: proposal-discard-binding (https://github.com/tc39/proposal-discard-binding). This proposal introduces the

void

operator as a binding that represents no value. In function calls or destructuring,

void

can be used to ignore a value.

Example of destructuring:

<code>const { z: void, ...obj1 } = { x: 1, y: 2, z: 3 };
obj1; // { x: 1, y: 2 }</code>

Function call examples:

<code>// project an array values into an array of indices
const indices = array.map((void, i) => i);

// passing a callback to Map.prototype.forEach that only cares about keys
map.forEach((void, key) => { });

// watching a specific known file for events
fs.watchFile(fileName, (void, kind) => { });

// ignoring unused parameters in an overridden method
class Logger {
  log(timestamp, message) {
    console.log(`${timestamp}: ${message}`);
  }
}
class CustomLogger extends Logger {
  log(void, message) {
    // this logger doesn't use the timestamp...
  }
}</code>

Iterator chunking

Proposal: proposal-iterator-chunking (https://github.com/tc39/proposal-iterator-chunking). Adds a new iteration method to ECMAScript iterators that can produce overlapping (sliding window) or non‑overlapping sub‑sequences (chunking) with a configurable size.

Chunking is similar to

_.chunk

from lodash.

<code>const digits = () => [0,1,2,3,4,5,6,7,8,9].values();

let chunksOf2 = Array.from(digits().chunks(2));
// [ [0,1], [2,3], [4,5], [6,7], [8,9] ]

let chunksOf3 = Array.from(digits().chunks(3));
// [ [0,1,2], [3,4,5], [6,7,8], [9] ]

let chunksOf4 = Array.from(digits().chunks(4));
// [ [0,1,2,3], [4,5,6,7], [8,9] ]</code>

Typical use cases include pagination, grid layouts, matrix operations, formatting/encoding, and bucketing.

The proposal also supports a sliding‑window mode.

<code>let chunksOf2 = Array.from(digits().chunks(2));
let chunksOf3 = Array.from(digits().chunks(3));
let chunksOf4 = Array.from(digits().chunks(4));</code>

ESM Phase Imports

Proposal: proposal-esm-phase-imports (https://github.com/tc39/proposal-esm-phase-imports), a supplement to proposal-source-phase-imports (https://github.com/tc39/proposal-source-phase-imports). It improves worker runtime and toolchain support by allowing static import of modules for Worker initialization.

Current

new Worker()

requires a URL, which is not a static import and may involve relative URLs resolved against

import.meta.url

, making analysis difficult.

<code>// Common case, but requires non‑trivial static analysis
const url = new URL("./my_worker.js", import.meta.url);
const worker = new Worker(url, { type: "module" });

const url2 = import.meta.resolve("./my_worker.js");
const worker2 = new Worker(url2, { type: "module" });

function createWorker(url) {
  return new Worker(url, { type: "module" });
}
const processor = createWorker(url);
</code>

The proposal defines a source‑phase import record so a module can be passed directly to

new Worker

:

<code>import source myModule from "./my-module.js";

const worker = new Worker(myModule);
</code>

Dynamic import is also supported:

<code>const workerModule = await import.source('./worker.js');
new Worker(workerModule);
</code>

Extractors

Proposal: proposal-extractors (https://github.com/tc39/proposal-extractors). Introduces extractors to enhance

BindingPattern

and

AssignmentPattern

syntax, allowing new forms of destructuring.

During destructuring, the extractor’s

Symbol.customMatcher

is invoked to transform and return the result.

<code>function toDate(value) {
  if (value instanceof Date) {
    return value;
  }
  if (typeof value === 'string' || typeof value === 'number') {
    return new Date(value);
  }
  return new Date();
}
const now = Date.now();
const date = toDate(now);
console.log(date.getFullYear());
</code>

With extractors:

<code>const ToDateExtractor = {
  [Symbol.customMatcher](value) {
    if (value instanceof Date) {
      return [value];
    }
    if (typeof value === 'string' || typeof value === 'number') {
      return [new Date(value)];
    }
    return [new Date()];
  }
};

const now = Date.now();
const ToDate(date) = now;
console.log(date.getFullYear());
</code>

Supported usage examples include binding patterns and assignment patterns such as:

<code>// binding patterns
const Foo(y) = x;
const Foo([y]) = x;
const Foo({y}) = x;
const [Foo(y)] = x;
const { z: Foo(y) } = x;
const Foo(Bar(y)) = x;
const X.Foo(y) = x;

// assignment patterns
Foo(y) = x;
Foo([y]) = x;
Foo({y}) = x;
[Foo(y)] = x;
({ z: Foo(y) } = x);
Foo(Bar(y)) = x;
X.Foo(y) = x;
</code>

Structs

Proposal: proposal-structs (https://github.com/tc39/proposal-structs). Introduces

Structs

,

Shared Structs

,

Mutex

,

Condition

, and

Unsafe Blocks

to bring multithreading and shared memory capabilities to JavaScript, offering a higher‑performance alternative to classes.

Structs

Non‑shared structs are sealed objects (equivalent to

Object.seal(obj)

) and cannot have new fields added.

<code>struct Box {
  constructor(x) { this.x = x; }
  x;
}
let box = new Box(0);
box.x = 42; // x is declared
assertThrows(() => { box.y = 8.8; }); // structs are sealed
assertThrows(() => { box.__proto__ = {}; }); // structs are sealed
</code>

Shared Structs

Shared structs can be transferred between JavaScript agents via

MessagePort

. They may only contain primitive values or other shared structs and have no instance methods or private names.

<code>// main.js
shared struct SharedBox {
  x;
}
let sharedBox = new SharedBox();
let sharedBox2 = new SharedBox();

unsafe {
  sharedBox.x = 42;          // primitive
  sharedBox.x = sharedBox2; // shared struct
  assertThrows(() => { sharedBox.x = {}; }); // not shared
}
assert(Reflect.canBeShared(sharedBox2));
assert(!Reflect.canBeShared({}));

let worker = new Worker('worker.js');
worker.postMessage({ sharedBox });

unsafe {
  sharedBox.x = "main";
  console.log(sharedBox.x);
}
</code>
<code>// worker.js
onmessage = function(e) {
  let sharedBox = e.data.sharedBox;
  unsafe {
    sharedBox.x = "worker";
    console.log(sharedBox.x);
  }
};
</code>

Mutex and Condition

The proposal adds

Atomics.Mutex

(a non‑recursive mutex) and

Atomics.Condition

(a condition variable) as shared structs with static methods for synchronization.

<code>shared struct MicrosoftSharePoint {
  x;
  y;
  mutex;
}
let point = new MicrosoftSharePoint();
point.mutex = new Atomics.Mutex();

let worker = new Worker('worker_mutex.js');
worker.postMessage({ point });

unsafe {
  using lock = Atomics.Mutex.lock(point.mutex);
  point.x = "main";
  point.y = "main";
}
unsafe {
  using lock = Atomics.Mutex.lock(point.mutex);
  console.log(point.x, point.y);
}
</code>
<code>// worker_mutex.js
onmessage = function(e) {
  let point = e.data.point;
  unsafe {
    using lock = Atomics.Mutex.lock(point.mutex);
    point.x = "worker";
    point.y = "worker";
  }
};
</code>

Unsafe Blocks

Operations on shared structs must occur inside

unsafe

blocks to avoid data races.

<code>shared struct Counter {
  value = 0;
}
const ctr = new Counter(); // allocation allowed
assertThrows(() => ctr.value = 1); // error (writes shared memory)
assertThrows(() => ctr.value);    // error (reads shared memory)

unsafe {
  ctr.value = 1; // ok
  ctr.value;     // ok
}

function incrementCounter(ctr, mutex) {
  unsafe {
    using lck = Atomics.Mutex.lock(mutex);
    ctr.value++;
  }
}
</code>
JavaScriptconcurrencyECMAScriptLanguage ProposalsStructs
Taobao Frontend Technology
Written by

Taobao Frontend Technology

The frontend landscape is constantly evolving, with rapid innovations across familiar languages. Like us, your understanding of the frontend is continually refreshed. Join us on Taobao, a vibrant, all‑encompassing platform, to uncover limitless potential.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.