stage-0 to stage-4 ECMAscript proposals.
TOC:
- Stage 0:
- Defensible Classes
 - Relationships
 - String.prototype.at
 - Reflect.isCallable
 - Reflect.isConstructor
 - Additional metaproperties
 - Function Bind Syntax
 - 64-Bit Integer Operations
 - Method Parameter Decorators
 - Function Expression Decorators
 - Zones
 - Object enumerables
 - Nested import declarations
 - is{Type} APIs
 - Orthogonal Class Member Syntax
 - Pattern Matching Syntax
 - Structured cloning and transfer
 - WHATWG URL
 
 - Stage 1:
- export v from "mod"; statements
 - Observable
 - String.prototype.matchAll
 - WeakRefs
 - Frozen Realms
 - Math Extensions
 ofandfromon collection constructors- Generator arrow functions.
 - Date.parse fallback semantics
 - Generator arrow functions (=>*)
 - Promise.try
 ofandfromon collection- Optional Chaining
 - Math.signbit: IEEE-754 sign bit
 - Error Stacks
 - do expressions
 - Realms
 
 - Stage 2:
 - Stage 3:
- global
 - Rest and Spread properties
 - Async-iteration
 - Function.prototype.toString revision
 - SIMD APIs
 - Lifting Template Literal Restriction
 - Shared memory and atomics
 - global
 - import()
 - RegExp Lookbehind Assertions
 - Unicode property escapes in RE
 - RegExp Named Capture Groups
 - s (dotAll) flag for regular expressions
 - Asynchronous Iterators
 - Promise.prototype.finally
 - Class Fields
 
 
Stage-0
// const class
const class Point { 
  constructor(x, y) {
    public getX() { return x; }
    public getY() { return y; }
  }
  toString() { 
    return `<${this.getX()}, ${this.getY()}>`;
  }
}Stage-0
x @ r // The object x is in the r relationship with what value?
x @ r = y; // Store that x is in the r relationship with value y.Stage-0
'abc𝌆def'.at(3)
// → '𝌆'Stage-0
Reflect.isCallable(argument);Stage-0
Reflect.isConstructor(argument)Stage-0
function.callee; // function object that is currently being evaluated by the running execution context.function.count; // number of arguments pass to the function. function.arguments; // array containing the actual arguments passed to the function.Stage-0
// :: which performs this binding and method extraction.
Promise.resolve(123).then(::console.log);Stage-0
// return the high 32 bit part of the 64 bit addition of (hi0, lo0) and (hi1, lo1)
Math.iaddh(lo0, hi0, lo1, hi1);
// return the high 32 bit part of the 64 bit subtraction of (hi0, lo0) and (hi1, lo1)
Math.isubh(lo0, hi0, lo1, hi1);
// return the high 32 bit part of the signed 64 bit product of the 32 bit numbers a and b
Math.imulh(a, b);
// return the high 32 bit part of the unsigned 64 bit product of the 32 bit numbers a and b
Math.umulh(a, b);Stage-0
//decorators that operate on method and constructor parameters.
class MyComponent {
  refresh(@lastRefreshTime timeStamp) { … }
}
export function lastRefreshTime(...) {
  // at minimum, the arguments of this function should contain:
  // - reference to owner of the parameter (the method)
  // - parameter index
  // - parameter name
  // - is parameter a rest parameter?
  // store parameter metadata using the same storage mechanism
  // as the one used for methods
}Stage-0
scheduleForFrequentReexecution(@memoize function(value) { 
  value++
});
export function memoize(...) {
  // at minimum, the arguments of this function should contain:
  // - reference to the decorated function expression
  // - arguments passed into the memoize function (if any)
  // wrap the decorated function expression memoization implementation and return it
}Stage-0
//a primitive for context propagation across multiple logically-connected async operations
class Zone {
  constructor({ name, parent });
  name;
  get parent();
  fork({ name });
  run(callback);
  wrap(callback);
  static get current();
}
const loadZone = Zone.current.fork({ name: "loading zone" });
window.onload = loadZone.wrap(e => { ... });Stage-0
Object.enumerableKeys(obj); // Ordered list of keys.Object.enumerableValues(obj); // Ordered list of Values.Object.enumerableEntries(obj); //Ordered list of key value pairs.Stage-0
describe("fancy feature #5", () => {
  import { strictEqual } from "assert";
  it("should work on the client", () => {
    import { check } from "./client.js";
    strictEqual(check(), "client ok");
  });
  it("should work on the client", () => {
    import { check } from "./server.js";
    strictEqual(check(), "server ok");
  });
  it("should work on both client and server", () => {
    import { check } from "./both.js";
    strictEqual(check(), "both ok");
  });
});Stage-0
Builtin.is(Date, vm.runInNewContext('Date'));     // false
Builtin.typeOf([]);                             // 'Array'
Builtin.typeOf(new ArrayBuffer());              // 'ArrayBuffer'
Builtin.typeOf(async function foo() {}); 
// So on.Stage-0
//A kitchen sink example
class Foo {
  //instance members
  own x=0, y=0;  // two data properties
  own #secret;   // a private field
                 // initial value undefined
  own *[Symbol.iterator](){yield this.#secret}
                 // a generator method
  own #callback(){}  //a private instance method  
  //class constructor members               
  static #p=new Set(), q=Foo.#p;
                // a private field and a property
                // of the class constructor                     
  static get p(){return Foo.#p} //accessor method     
  //prototype methods                
  setCallback(f){this.#callback=f}
  constructor(s){
     this.#secret = s;
  }
}Stage-0
let getLength = vector => match (vector) {
    { x, y, z }: Math.sqrt(x ** 2 + y ** 2 + z ** 2),
    { x, y }:    Math.sqrt(x ** 2 + y ** 2),
    [...]:       vector.length,
    else: {
        throw new Error("Unknown vector type");
    }
}Stage-0
StructuredClone(input, transferList, targetRealm)Stage-0
const base = new URL('http://example.org/foo');
const url = new URL('bar', base);Stage-1
export v, {x, y as w} from "mod";
export v, * as ns from "mod";Stage-1
// Observable as a Constructor:
function listen(element, eventName) {
    return new Observable(observer => {
        // Create an event handler which sends data to the sink
        let handler = event => observer.next(event);
        // Attach the event handler
        element.addEventListener(eventName, handler, true);
        // Return a function which will cancel the event stream
        return () => {
            // Detach the event handler from the element
            element.removeEventListener(eventName, handler, true);
        };
    });
}
// Observable.of creates an Observable of the values provided as arguments
Observable.of("R", "G", "B").subscribe({
    next(color) {
        console.log(color);
    }
});
// Observable.from converts its argument to an Observable.
Observable.from(["R", "G", "B"]).subscribe({
    next(color) {
        console.log(color);
    }
});Stage-1
var str = 'Hello world!!!';
var regexp = /(\w+)\W*/g;
console.log(str.matchAll(regexp));
/*
[
  {
    0: "Hello ",
    1: "Hello"
    index: 0,
    input: "Hello world!!!"
  },
  {
    0: "world!!!",
    1: "world"
    index: 6,
    input: "Hello world!!!"
  }
]
*/Stage-1
// Make a new weak reference.
// The target is a strong pointer to the object that will be pointed
// at weakly by the result.
// The executor is an optional argument that will be invoked after the
// target becomes unreachable.
// The holdings is an optional argument that will be provided to the
// executor when it is invoked for target.
makeWeakRef(target, executor, holdings);Stage-1
class Realm {
  // From the prior Realm API proposal
  const global -> object                // access this realm's global object
  eval(stringable) -> any               // do an indirect eval in this realm
  // We expect the rest of earlier proposal to be re-proposed eventually in
  // some form, but do not rely here on any of the remainder.
  // New with this proposal
  static immutableRoot() -> Realm       // transitively immutable realm
  spawn(endowments) -> Realm            // lightweight child realm
}Stage-1
// Possible ones:
Math.map
Math.scale
Math.remap
Math.clamp
Math.constrain
Math.toDegrees(double angrad)
Math.toRadians(double angdeg)Stage-1
Map.of( ...items );
Map.from( ...items );
Set.of( ...items );
Set.from( ...items );
WeakMap.of( ...items );
WeakMap.from( ...items );
WeakSet.of( ...items );
WeakSet.from( ...items );Stage-1
let cat = *() => { yield 'meow'; }Stage-1
//  New grammar should be used as the "fallback" 
//  when date strings do not conform to the 
//  regular Date Time String Format.Stage-1
// current
x => x * x;
(...) => { statements }
(...) => ( expr )
// proposed generator arrows...
// Irregular
() =*>
// Hostile to ! (async function)
() => * { ...yield... }
// Not good
() => * (yield a, yield b)
// Ok if 1 token
x *=> x * x;
// Bad (ASI)
*() => ...
// Hostile to !
(x) =* {...}Stage-1
// Promise.try(function() fn) -> PromiseStage-1
CollectionCreate ( C, source [ , mapfn [ , thisArg ] ] )
Map.of ( ...items )
Set.of ( ...items )
WeakMap.of ( ...items )
WeakSet.of ( ...items )
Map.from ( source [ , mapFn [ , thisArg ] ] )
Set.from ( source [ , mapFn [ , thisArg ] ] )
WeakMap.from ( source [ , mapFn [ , thisArg ] ] )
WeakSet.from ( source [ , mapFn [ , thisArg ] ] )Stage-1
obj?.prop         // optional property access
obj?.[expr]       // ditto
func?.(...args)   // optional function or method call
new C?.(...args)  // optional constructor invocationStage-1
Math.signbit(x);
/*
Returns whether the sign bit of x is set.
If n is NaN, the result is false.
If n is -0, the result is true.
If n is negative, the result is true.
Otherwise, the result is false.
*/Stage-1
Error.prototype.stack;
System.getStack;
System.getStackString;
Object.getOwnPropertyDescriptor(new Error(), 'stack');
Object.getOwnPropertyDescriptor(Error.prototype, 'stack');Stage-1
let x = do {
  let tmp = f();
  tmp * tmp + 1
};
let x = do {
  if (foo()) { f() }
  else if (bar()) { g() }
  else { h() }
};Stage-1
let realm = new Realm();
let outerGlobal = window;
let innerGlobal = realm.global;
let f = realm.evalScript("(function() { return 17 })");
f() === 17 // true
Reflect.getPrototypeOf(f) === outerGlobal.Function.prototype // false
Reflect.getPrototypeOf(f) === innerGlobal.Function.prototype // true
class EmptyRealm extends Realm {
  constructor(...args) { super(...args); }
  init() { /* do nothing */ }
}
class FakeWindow extends Realm {
  init() {
    super.init(); // install the standard primordials
    let global = this.global;
    global.document = new FakeDocument(...);
    global.alert = new Proxy(fakeAlert, { ... });
    ...
  }
}Stage-2
// The proposal is about fixing those Illegal token errors, avoid restrictions on escape sequences. 
let document = latex`
\newcommand{\fun}{\textbf{Fun!}}  // works just fine
\newcommand{\unicode}{\textbf{Unicode!}} // Illegal token!
\newcommand{\xerxes}{\textbf{King!}} // Illegal token!
Breve over the h goes \u{h}ere // Illegal token!Stage-2
// Avoid ingnoring the first `next` call.
function *adder(total=0) {
   let increment=1;
   do {
       switch (request = function.sent){
          case undefined: break;
          case "done": return total;
          default: increment = Number(request);
       }
       yield total += increment;
   } while (true)
}
let tally = adder();
tally.next(0.1); // argument no longer ignored
tally.next(0.1);
tally.next(0.1);
let last=tally.next("done");
console.log(last.value);  //0.3Stage-2
// Class instance field
class MyClass {
  myProp = 42;
  constructor() {
    console.log(this.myProp); // Prints '42'
  }
}
// Static property
class MyClass {
  static myStaticProp = 42;
  constructor() {
    console.log(MyClass.myStaticProp); // Prints '42'
  }
}// Class Static Properties
class MyClass {
  static myStaticProp = 42;
  constructor() {
    console.log(MyClass.myStaticProp); // Prints '42'
  }
}Stage-2
class C {
  @writable(false)
  method() { }
}
function writable(value) {
  return function (target, key, descriptor) {
     descriptor.writable = value;
     return descriptor;
  }
}Stage-2
"    Hey JS!".trimStart(); // "Hey JS!"
"    Hey JS!    ".trimEnd();// "    Hey JS!"
// P.S: trimLeft/trimRight are aliases.Stage-2
RegExpAlloc( newTarget );
RegExpBuiltInExec( R, S );
RegExp.input;
RegExp.prototype.compile( pattern, flags ); // modificationsStage-2
// Create a segmenter in your locale
let segmenter = Intl.Segmenter("fr", {type: "word"});
// Get an iterator over a string
let iterator = segmenter.segment("Ceci n'est pas une pipe");
// Iterate over it!
for (let {segment, breakType} of iterator) {
  console.log(`segment: ${segment} breakType: ${breakType}`);
  break;
}
// logs the following to the console:
// index: Ceci breakType: letterStage-2
export * as ns from "mod";  // Exporting the ModuleNameSpace object as a named export.Stage-3
// global to rule them all.
var getGlobal = function () {
    // the only reliable means to get the global object is
    // `Function('return this')()`
    // However, this causes CSP violations in Chrome apps.
    if (typeof self !== 'undefined') { return self; }
    if (typeof window !== 'undefined') { return window; }
    if (typeof global !== 'undefined') { return global; }
    throw new Error('unable to locate global object');
};Stage-3
let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; // Rest.
let n = { x, y, ...z }; // Spread.Stage-3
asyncIterator
  .next()
  .then(({ value, done }) => /* ... */);Stage-3
// String's parse must contains the same
// function body and parameter list as the original.
O.gOPD({ get a(){} }, "a").get // "function a(){}"
O.gOPD({ set a(b){} }, "a").set // "function a(b){}"Stage-3
/*a meta-variable ranging over all SIMD types:
  Float32x4, Int32x4, Int16x8 Int8x16, Uint32x4, 
  Uint16x8, Uint8x16, Bool32x4, Bool16x8 and Bool8x16. */Stage-3
function tag(strs) {
  strs[0] === undefined
  strs.raw[0] === "\\unicode and \\u{55}";
}
tag`\unicode and \u{55}`
let bad = `bad escape sequence: \unicode`; // throws early errorStage-3
var sab = new SharedArrayBuffer(1024);  // 1KiB shared memory
w.postMessage(sab, [sab])
// In the worker:
var sab;
onmessage = function (ev) {
   sab = ev.data;  // 1KiB shared memory, the same memory as in the parent
}stage 3
typeof global; // object, helps in writing a portable code.stage 3
import(`./language-packs/${navigator.language}.js`) // import(specifier)Stage-3
const str = '1947';
// (?<=(\d+)(\d+))$/ => (947) and (1)
// Greediness proceeds from right to left
// match[1] => 947 and match[2] => 1
// Numbering capture groups
// /(?<=\1(.))/
// Referring to capture groups
// /(?<!.)/
// Negative assertionsStage-3
const regexGreekSymbol = /\p{Script=Greek}/u;
regexGreekSymbol.test('π');Stage-3
let {one, two} = /^(?<one>.*):(?<two>.*)$/u.exec('foo:bar');
console.log(`one: ${one}, two: ${two}`);  // prints one: foo, two: barStage-3
const re = /foo.bar/s; // Or, `const re = new RegExp('foo.bar', 's');`.
re.test('foo\nbar');
// → true
re.dotAll
// → true
re.flags
// → 's'
/foo.bar/s.test('foo\nbar');
// → trueStage-3
asyncIterator.next().then(result => console.log(result.value));
for await (let line of readLines(filePath)) {
    print(line);
}
async function *readLines(path) {
    let file = await fileOpen(path);
    try {
        while (!file.EOF)
            yield file.readLine();
    } finally {
        await file.close();
    }
}Stage-3
somePromise()
.then(() => {})
.catch(() => {})
.finally(() => {})Stage-3
class Point {
    #x = 0;  // private fields start with #
    myProp = 42; // public field
    constructor() {
        this.#x; // 0
        this.myProp; // 0
    }
}