ES2026 Lands, Temporal Ships, and Decorators Get Demoted: The State of JavaScript Standards
The Most Comprehensive ECMAScript Guide on the Web (Updated to ES2026)
This article was written in 2021 and is updated annually with each new standard release. Thanks to Claude's assistance, this year's edition has undergone the largest update ever: a detailed breakdown of each new ES2026 feature, a complete reversal to descending chronological order, and deep dives into TC39 proposals and Ecma member dynamics.
First, take a minute to catch up on what happened in the JavaScript standards world over the past year:
- ES2026 Officially Approved: On June 30, the Ecma General Assembly approved ECMA-262 17th Edition. All 7 new features are standard library enhancements (
getOrInsert,Iterator.concat, native Base64,Array.fromAsync...). Details below. - Temporal's Nine-Year Marathon Ends: It finally reached Stage 4 in March, but unfortunately missed the ES2026 cutoff and is slated for ES2027. The good news is that Firefox and Chrome already support it. Preview below.
- Decorators Rarely Downgraded: After waiting four years without native browser implementation, it was demoted from Stage 3 back to 2.7 in May. During the same period, Records & Tuples was officially withdrawn. Proposal chronicle below.
- Ecma Membership Shake-up: Hardware giants like NVIDIA and AMD joined en masse, while ByteDance exited. Total membership hit a record high of 92. Member dynamics below.
- A Fun Fact: In December 2025, Ecma published a full suite of NLIP standards. "Natural Language Interaction" for AI Agents is also beginning to be standardized.
As a front-end developer, the term ES6 is likely familiar, and you probably know the oddly pronounced name ECMAScript. But have you ever been curious about the mysterious numeric codes of the ECMA world? What is ECMA-262? What is ESxxx? What is TC39? What do Stage 3 and Stage 4 mean? Are you confused by the endless stream of new ES20XX features, wondering which year a feature was introduced, where to find these proposals, which ones are usable, and where to look up past ES standards? Today, I'll unveil the mystery of ECMAScript, helping you thoroughly understand these cryptic codes and all ECMAScript features up to 2026.
Also: Congratulations to China's first JS language proposal, which entered Stage 3 in April 2021, successfully reached Stage 4 in November 2021, and was officially released in ECMAScript 2022, becoming a de facto standard. See Error Cause.
On June 30, 2026, the 131st Ecma International General Assembly in Geneva approved ECMA-262 17th Edition — the ECMAScript® 2026 standard. The same meeting also approved ECMA-402 13th Edition (ECMAScript Internationalization API). The full specification can be read online at tc39.es/ecma262/2026.
ECMAScript History
Let's first look at what ECMA is. ECMA, pronounced like "Ek-ma," is the abbreviation for the European Computer Manufacturers Association, an international membership-based information and telecommunications standards organization. After 1994, as the organization's standards involved many other countries, to reflect its international nature, it was renamed Ecma International. Thus, Ecma is no longer an acronym.
Understanding this history, for the professionalism of technical writing, if you mention Ecma in an article, the standard way to write it is Ecma (the official logo uses lowercase ecma), not all-caps ECMA, unless it's a proper noun and standard number like ECMAScript or ECMA-262.
In 1995, Brendan Eich of the famous Netscape company developed a scripting language, initially named Mocha, later renamed LiveScript, and finally renamed JavaScript to ride the popularity of the then-hot Java.
With an understanding of Ecma International and JavaScript, it's easier to understand ECMAScript. ECMAScript is a scripting language specification defined by Ecma International in the standard ECMA-262. This language is often referred to as JavaScript or JScript (Microsoft's dialect from the IE era, now history), but in reality, they are implementations and extensions of the ECMA-262 standard.
The Mysterious ECMA-262
The first mysterious code mentioned above is ECMA-262. What exactly is ECMA-262? It turns out that Ecma International's standards are all named with "ECMA-number" (the standard number retains the historical all-caps writing). ECMA-262 is the 262nd standard, specifically referring to the standard that ECMAScript complies with. In November 1996, Netscape submitted JavaScript to Ecma International for standardization. The first version of ECMA-262 was adopted by Ecma International in June 1997.
Although JavaScript and JScript are compatible with ECMAScript, they contain features beyond ECMAScript.
How can we view the latest and most complete Ecma standards? You can check the Standards page on the Ecma International website. As of July 2026, the latest Ecma standard has reached ECMA-434. Interestingly, ECMA-430 ~ 434 are a full suite of NLIP (Natural Language Interaction Protocol) standards and their security configurations and transport bindings, approved in December 2025. In the AI Agent era, even "natural language interaction" itself is beginning to be standardized. The same batch also includes ECMA-429 (Minimum common web API, defining a minimal common Web API set for various JS runtimes).
Ecma standards cover a wide range of categories. The official website provides the ability to sort by category and latest modification. Let's see which category ECMA-262 belongs to:
ECMA-262 belongs to the "Software Engineering and Interfaces" category, which has a total of 22 standards, as shown in the image above (ECMA-427, 428, and 429, added in December 2025, are the Package-URL specification, Common Lifecycle Enumeration CLE, and Minimum common web API, respectively; the NLIP series is not in this category). Note that the latest update date for ECMA-262 is June 2026, and the current latest standard is ES2026.
Exploring the Mysterious Organization: Ecma TC39
After unveiling the mystery of Ecma-262, let's explore a mysterious organization codenamed TC39.
Actually, the official website explains it quite clearly. I'll briefly summarize in Chinese:
TC39 is the abbreviation for Technical Committee 39, the committee responsible for developing the ECMAScript standard. It is composed of representatives sent by Ecma member organizations: browser vendors are the core, but representatives from financial companies, open-source consulting firms, and universities are also among them (the extended reading below will elaborate). The chair group of three comes from IBM, Bloomberg, and Igalia (current in 2026), and there are 5 Task Groups:
- TC39-TG1 (General ECMAScript® Language, including syntax, semantics, class libraries, and technologies supporting the language)
- TC39-TG2 (ECMAScript® Internationalization API Standard)
- TC39-TG3 (Security)
- TC39-TG4 (Source map standard)
- TC39-TG5 (Experiments in Programming Language Standardization)
We often see news like: XX company becomes a member of Ecma TC39. To participate in TC39 meetings as a member, you must first join Ecma (after the Ecma website revamp, this participation guide was moved to the TC39 website homepage, which also conveniently places the six-stage process and meeting minutes entry points together):
So, who exactly are the members of Ecma? The Ecma official website provides the answer:
Several giants are prominently listed! The 6 Ordinary members in 2026: Apple, Bloomberg, Google, Huawei, IBM, Meta. Here's a key point to clarify a common misunderstanding (which even earlier versions of this article didn't state precisely): There is only one type of membership, which is Ecma membership. There is no separate "TC39 membership." Ecma membership has 5 categories: Ordinary members, Associate members, SME members, SPC members, and NFP members. After paying dues to Ecma, participating in any technical committee like TC39 or TC54 incurs no extra charge, although joining TC39 requires signing an additional patent royalty-free agreement (RFTC registry, no cost). Let's look at the comparison between Ordinary and Associate members:
Associate members do not have voting rights at the Ecma General Assembly! Among Associate members, China has 2 more: Alibaba and 360 (compared to 2021, Tencent is missing; ByteDance was listed between 2021-2025 but has exited the membership list in 2026).
Wait, CHF 70,000, is that 70,000 Swiss Francs? Ecma is indeed a European heavyweight. The annual fee for an Ordinary member is about 590,000 RMB (based on the July 2026 exchange rate of 1 CHF ≈ 8.4 CNY). A quick calculation shows Ecma International's annual membership fee income is 1,260,000 Francs, breaking the ten-million RMB mark.
To learn more about TC39, you can explore its official website TC39 – Specifying JavaScript. and its GitHub repository Ecma TC39 · GitHub. Note that this repository is very important. Checking the members, you can find three domestic experts: Yue Ying, Li Songfeng, and Yuan Yan.
Latest Ecma Membership Updates (2026)
As of July 2026 (Ecma Members Page), the membership landscape has several noteworthy changes:
- 6 Ordinary members: Apple, Bloomberg, Google, Huawei, IBM, Meta. Huawei maintains its status as China's only Ordinary member.
- Chinese Enterprise Changes: Alibaba and 360 remain Associate members; ByteDance has exited the membership list (was still an Associate member in 2025); Sujitech (Mask Network) remains a Small Private Company member.
- Associate Member Roster Expansion: NVIDIA, AMD, Oracle, Salesforce, ServiceNow, Shopify, Sony Interactive Entertainment, JetBrains, Dell, Hitachi, Lockheed Martin, F5, Consensys, Functional Software (Sentry), and others have joined in succession. The most conspicuous change in the past two years is the collective entry of hardware/AI vendors and enterprise software giants.
- SME Members: Besides Igalia and Vercel, HeroDevs (focusing on long-term EOL maintenance for open-source software) is a new addition.
- Non-Profit Members (NFP) reach 51: Including Mozilla, Apache Software Foundation, OpenJS Foundation, OWASP, etc.
The total number of members has grown from about 70 in 2021 and 86 in 2025 to 92. Wait, what are graphics card makers NVIDIA and AMD doing in a JavaScript standards organization? And why did ByteDance quietly leave? Read the extended reading below.
Extended Reading: What Are the Giants Really Here For?
To answer the two questions above, I had Claude's research Agent comb through Ecma's yearbook (Memento, published annually, listing registered representatives by committee) and TC39's public meeting minutes—over 90 searches, each demanding a primary source. This kind of grunt work is exactly what an Agent excels at. The answers are more interesting than expected.
First, correct an intuition: Ecma ≠ TC39. In recent years, Ecma has quietly opened several new tables, and new members have different seats:
- TC54 (Software Supply Chain Transparency): Established in December 2023, triggered by OWASP donating the de facto SBOM standard CycloneDX to Ecma for standardization (now ECMA-424). The committee chair is CycloneDX founder Steve Springett of ServiceNow. Lockheed Martin's 4 registered representatives are all seated at this table, alongside Socket, IBM, Google, Apple, and Apache. The driving force is the EU's Cyber Resilience Act (CRA): vulnerability reporting obligations take effect in September 2026, with full enforcement by December 2027. Selling software in the EU requires a machine-readable SBOM. OWASP officially linked TC54 standardization to CRA compliance. In short: Compliance pressure pushed enterprise software giants into the standards meeting room.
- TC55 (Web Interoperable Server Runtimes): This is the community group WinterCG turned official as WinterTC, chaired by Deno's Luca Casonato. Cloudflare, Vercel, Shopify, and Alibaba are participating. It produced its first standard, ECMA-429 (Minimum Common Web API), in December 2025.
- TC56 (NLIP, AI Agent Natural Language Interaction Protocol): Led by IBM (Chair Dinesh Verma), with ServiceNow, Hitachi, Microsoft, and several universities on board. In December 2025, it passed ECMA-430 ~ 434 five-peat in one go.
- TC57 (HLSL Shader Language): In February 2026, Microsoft announced it was handing DirectX's HLSL to Ecma for vendor-neutral standardization, explicitly stating "platform parties can participate equally." Four months later, at the first general assembly, NVIDIA and AMD both joined. Neither stated their intentions (neither Ecma announcements nor their own blogs mentioned a word), but the timing suggests they are likely here to "vote on the GPU language," not JavaScript—this is a reasonable inference pending the 2027 yearbook committee lists.
So the answer to the first question is revealed: The graphics card makers did indeed take a seat in Ecma's meeting room, but probably not in TC39's. Two fun facts uncovered while flipping through the yearbook: Dell joined as early as 2019, with its registered representative serving as Vice Chair of the TC26 Acoustics Committee, specializing in small fan noise—nothing to do with JS. F5's membership was inherited from its 2020 acquisition of Shape Security. Kevin Gibbons, who contributed two features to ES2026, wears an F5 badge.
Now, look at the departures:
When ByteDance joined in June 2021, the TC39 secretary left a remarkably blunt record in the meeting minutes: "Honestly speaking I have not heard about them." Over the next four years, ByteDance registered 8 representatives in the yearbook, but the Agent performed a full-text search of all public TC39 minutes from 2021-2026: zero speeches, zero proposals, zero sign-in records. The membership expired at the end of 2025 and was not renewed (website member page snapshots show it present in November 2025, gone by January 2026), a silent exit. Its real standards circle intersection was actually WinterCG (the community precursor to TC55, whose participant list still lists ByteDance). Its JS engine PrimJS (the runtime for the Lynx framework) only needs to implement ECMAScript, not hold membership.
This isn't an isolated case: Tencent joined in 2020, exited in 2023, also with zero hits in public minutes. In a word: An annual fee of less than 300,000 RMB buys a seat at the table easily; the hard part is actually sending someone to the meetings. In contrast: Alibaba's Error Cause proposal made it into ES2022, and Huawei holds Ordinary member voting rights at the General Assembly—the presence of Chinese companies at this table has never been determined by membership fees.
To answer a practical question: Can you directly check the "TC39 member list" on the official website? The closest page is the TC39 Royalty-Free Technical Committee Members list, but note that it's a historical cumulative list: once signed, you're on it, about 80 companies. Those who leave aren't removed, just marked with an asterisk as "Former Ecma member"—ByteDance and Tencent are still listed there. To see which companies currently have representatives registered for TC39, you have to dig through the annual Memento yearbook. This list also provides a small piece of evidence: Neither NVIDIA nor AMD has signed this TC39 agreement, indirectly confirming their entry wasn't for JavaScript.
Conversely, who is actually doing the work in TC39? A sketch based on the 2026 yearbook and proposal records:
- Engine vendors, holding de facto veto power: Google (V8), Apple (JavaScriptCore), Mozilla (SpiderMonkey, participating as a Non-Profit member). Interestingly, Microsoft is only an Associate member now, not among the six Ordinary members.
- Chair group and editors: The chair group comes from IBM, Bloomberg, and Igalia. Of the three editors for the ES2026 specification, one is at Google (Shu-yu Guo), and the other two are at F5 (Michael Ficarra, Kevin Gibbons).
- The biggest spender: Bloomberg. A Wall Street terminal company, yet it consistently funds the implementation of proposals. The engineering implementation of Temporal was funded by Bloomberg and undertaken by Igalia (an SME member, a Spanish open-source consultancy, TC39's "mercenary engineering team").
- Proposal powerhouses: Just look at the employers of the authors of ES2026's seven features: Mozilla (Upsert), F5 (Base64 and sumPrecise, two from one company), Socket (Jordan Harband for Error.isError, one of the most prolific champions in the community).
- Nameplate easter egg: Oracle is also on the Associate member list, but its presence in the JS world mainly involves holding the "JavaScript" trademark. Deno petitioned the US Patent and Trademark Office to cancel it at the end of 2024, and the case was still ongoing as of mid-2026. The name JavaScript still doesn't belong to the people who write JavaScript.
The Familiar ES6
After exploring the mysterious ECMA-262 and TC39, let's take a breather and look at the ESX family we're most familiar with.
As mentioned above, ECMAScript is a scripting language specification defined by Ecma International in the standard ECMA-262. By 2015, a total of 6 versions had been officially released: 1, 2, 3, 5, 5.1, and 6 (the 4th edition died in the womb and was never released, leaving the version number vacant; see the ES6 inaugural year special below for this story).
We often collectively refer to the 5th edition and the 5.1 revision as ES5, and the 6th edition and subsequent versions as ES6 (because starting from 2015, ECMAScript finally got on track, releasing a version every year. To cope with the annual incrementing versions, ES6, which revolutionized the JavaScript era, is used as a proxy name for subsequent versions).
Key point: In web front-end job descriptions, the frequently seen ES6 does not just refer to the ES2015 version, but to ES2015 and the ECMAScript versions released annually thereafter.
Starting from ECMAScript 6th Edition, one ECMAScript version is released each year. Therefore, ECMAScript versions have many names, including the full name ECMAScript 6, the shorthand ES6, the year-based name ECMAScript 2015, and the year shorthand ES2015. The most common name is still ES6, and the same logic applies to subsequently launched ES7, ES8, etc. Note that versions after ES6 are basically referred to by their year shorthand; ES7, ES8, and similar shorthands are not commonly used.
It's important to note that since the establishment of the TC39 process, the importance of ECMAScript versions has greatly diminished. You don't need to remember which year a particular ES feature was introduced. What truly matters now is which stage a proposal is in: Once a proposal reaches Stage 4, it is ready to be used. However, even so, you still need to check if your engine supports that feature.
This brings up the concepts of the TC39 process and Stages. Let's look at what these two concepts mean next.
TC39 Process and Stage X
The TC39 process, as the name suggests, is a process published by the TC39 organization. With the release of ECMAScript 6, the release process at the time had two obvious problems:
- If features that were ready long ago were passed between two releases, they would have to wait a long time before their release. Moreover, if features were prepared late, it increased the risk of rushing before the deadline.
- Many features spent a long time on design before implementation and use, leading to very late discovery of design flaws related to implementation and use.
To solve these problems, TC39 established a new TC39 process:
- ECMAScript feature design is independent of the annual ECMAScript version release, using different Stages to distinguish the status of features. After November 2023, a new Stage 2.7 was added, making a total of 6 stages, from Stage 0 (strawperson) to Stage 4 (finished).
- Later stages require prototype implementations and real-world testing, establishing a feedback loop between design and implementation.
- ECMAScript versions are released once a year, containing all features that reached Stage 4 before the release deadline.
Evolution of the TC39 Process
The New 6-Stage Process After November 2023
The Classic 5-Stage Process from 2015-2023
- Stage 0: Codename "strawman," draft stage
- Stage 1: Codename "proposal," TC39 assistance stage
- Stage 2: Codename "draft," likely to become a standard at this stage
- Stage 3: Codename "candidate," completed, needs feedback from implementations
- Stage 4: Codename "finished," ready to become a standard
Why Add Stage 2.7?
On November 30, 2023, the TC39 committee decided to add Stage 2.7 to the process, primarily based on the following considerations:
- Separation of Concerns: Separating "specification completion" (Stage 2) from "test writing" (Stage 2.7) makes the goals of each stage clearer.
- Increased Certainty: When a proposal reaches Stage 2.7, the specification is frozen, allowing developers to start trying it out with more confidence.
- Optimized Review Process: Reviewers need to complete their sign-off before Stage 2.7, avoiding repeated changes later.
Regarding why the number 2.7 was chosen, TC39 gave an interesting explanation:
- Not renumbering avoids making historical documents difficult to understand.
- Choosing
.7over.5is because this stage is closer to Stage 3. - Not using 3a/3b avoids confusion.
A Milestone for a Chinese Proposal
Understanding the meaning of each Stage helps understand the significance of the news that China's first JS language proposal entered Stage 3 at ECMA. In April 2021, the Error Cause proposal, put forward by Alibaba's Frontend Standardization Group and Taobao Technology, entered Stage 3. This meant:
- The proposal specification was complete and would not undergo major changes.
- JavaScript engines began experimental implementation.
- This was the first ECMAScript proposal from China to advance to such a high stage.
- The proposal ultimately became a formal standard (Stage 4) in ES2022.
How to Track Proposal Progress?
To understand the latest status of various proposals, you can visit the following resources:
- TC39 Proposals - View the current stage of all proposals.
- TC39 ECMA262 - The official repository for the ECMAScript specification.
- TC39 Meeting Notes - TC39 meeting records to understand the details of proposal discussions.
💡 Developer Advice: Starting from ES2016, stop thinking in terms of "Does this browser support ES20XX?" Instead, focus on specific features: "Has this feature reached Stage 4? Does the target environment support it?" This way of thinking is more aligned with the modern JavaScript development model.
Complete Collection of ES Features Through the Years (ES2026 → ES2015)
There are too many scattered ES feature summaries online. Many students wonder, is there an official list of ECMAScript features?
Of course, there is. The TC39 repository lists finished proposals and their versions. In the 11 years since the release of ES6 in 2015, as of July 2026, this list contains a total of 78 entries (where the Class Fields entry merges 3 proposals), with another 4 proposals having reached Stage 4 and set to be released with ES2027 (see the next chapter).
This chapter is arranged in descending chronological order: the latest ES2026 is placed first, tracing all the way back to the inaugural year of ES6 in 2015. Want to check a specific year? Just scroll to it. Before diving year by year, let's look at the big picture.
A Table Summarizing 12 Versions:
| Version | Released | ECMA-262 | Features | Representative Features | One-Liner Theme |
|---|---|---|---|---|---|
| ES2026 | 2026-06 | 17th Ed. | 7 | getOrInsert, Iterator.concat, Native Base64 |
Standard Library Gap Fill |
| ES2025 | 2025-06 | 16th Ed. | 10 | Iterator Helpers, Set Methods, RegExp.escape |
The Year of Iterators & Regex |
| ES2024 | 2024-06 | 15th Ed. | 7 | Object.groupBy, Promise.withResolvers, RegExp v |
Grouping & Concurrency Tools |
| ES2023 | 2023-06 | 14th Ed. | 4 | findLast, toSorted and other immutable methods |
Immutable Arrays |
| ES2022 | 2022-06 | 13th Ed. | 8 | Class Fields, Top-level await, .at(), Error Cause |
Class Maturity + China's First Proposal |
| ES2021 | 2021-06 | 12th Ed. | 5 | replaceAll, Promise.any, Logical Assignment |
Small but Beautiful |
| ES2020 | 2020-06 | 11th Ed. | 9 | Optional Chaining ?., Nullish Coalescing ??, BigInt, import() |
Syntax Highlight Year |
| ES2019 | 2019-06 | 10th Ed. | 8 | flat / flatMap, Object.fromEntries |
Array & Object Completion |
| ES2018 | 2018-06 | 9th Ed. | 8 | Async Iteration, Object rest/spread, Regex Quartet | Async Iteration + Regex Year |
| ES2017 | 2017-06 | 8th Ed. | 6 | async/await, Object.entries, Shared Memory |
The Year of async/await |
| ES2016 | 2016-06 | 7th Ed. | 2 | includes, ** operator |
Minimum Viable Release |
| ES2015 | 2015-06 | 6th Ed. | 20+ | Class, Modules, Arrow Functions, Promise, let / const |
The Syntax Big Bang |
Feature Count Trend:
Spreading out these 78 entries over 11 years reveals three clear threads:
- Syntax is receding, the standard library is advancing. The last major syntax change that altered how we write code was Class Fields in ES2022 (the next one,
using, will have to wait for ES2027). ES2026's 7 features are all APIs. The language's skeleton is largely set; now it's about adding flesh and blood. - "Specification release" increasingly looks like "retroactive recognition of a fait accompli". Stage 4 requires two engine implementations and passing test262 tests, so features are generally usable first and enter the standard later:
Array.fromAsyncwas usable in the three major browsers in 2023 but only written into the spec in 2026; Temporal waited 9 years. So don't ask "Does the browser support ES2026?" Ask "Has this feature reached Stage 4?" - Iterators are becoming first-class citizens. Iterator Helpers in ES2025,
concatin ES2026,zipin ES2027, plus chunking/includes/join in the pipeline—TC39 is cashing in the iterator protocol planted by ES6 a decade ago, all at once, into a full suite.
Alright, let's dive year by year, starting with the latest ES2026.
ES2026 New Features
Officially released with ECMA-262 17th Edition on June 30, 2026 (TC39 approved the candidate specification at the March 2026 meeting; the Ecma General Assembly was just the final procedural step), it includes a total of 7 new features. If ES2025's theme was "iterators and regex," ES2026's theme is filling high-frequency gaps in the standard library: no major syntax-level moves, but each one eliminates a piece of boilerplate code you've written countless times.
First, a family portrait:
| # | Feature | One-Liner Summary | Stage 4 Date |
|---|---|---|---|
| 1 | Upsert (getOrInsert) | Get a value from a Map, insert a default if missing, in one step. | 2026-01 |
| 2 | JSON.parse source text access | The reviver can access the raw source text, eliminating large integer precision loss. | 2025-11 |
| 3 | Iterator Sequencing | Iterator.concat lazily concatenates multiple iterators. |
2025-11 |
| 4 | Uint8Array to/from Base64 | Native binary ↔ Base64/Hex conversion, goodbye atob. |
2025-07 |
| 5 | Math.sumPrecise | Floating-point summation without cumulative error. | 2025-07 |
| 6 | Error.isError | Reliably identify a genuine Error across realms. | 2025-05 |
| 7 | Array.fromAsync | The async version of Array.from. |
2025-05 |
Compatibility data in this section is as of July 2026, sourced from MDN BCD with some manual verification; Bun data is from hands-on testing with Bun 1.3.14.
Let's break them down one by one.
- Authors: Daniel Minor (Mozilla), Lauritz Thoresen Angeltveit et al. (A student team from the University of Bergen in Norway was deeply involved—a great story of TC39 school-enterprise cooperation)
- Champion: Daniel Minor
- TC39 Meeting Notes: January 2026 (The feature that caught the last train for ES2026)
- Description: Adds atomic "get value, insert if key doesn't exist then return" methods to Map and WeakMap.
- Core Problem Solved: Eliminates the
has→set→getboilerplate trio, an unavoidable pattern when using Maps for grouping, counting, or caching.
// ❌ Before: Three lines of boilerplate, anyone who's grouped with Map knows this
if (!groups.has(user.dept)) {
groups.set(user.dept, []);
}
groups.get(user.dept).push(user);
// ✅ ES2026: One step
groups.getOrInsertComputed(user.dept, () => []).push(user);
// For counter scenarios, use getOrInsert to provide a default value
counts.set(word, counts.getOrInsert(word, 0) + 1);
- Why it matters:
- 🎯 Two methods, each with a role:
getOrInsert(key, default)directly provides a default value;getOrInsertComputed(key, cb)only calls the factory function when the key is absent, avoiding unnecessary object construction overhead. - 🌍 Language alignment: Matches Python's
setdefaultand Rust'sentry().or_insert(). JavaScript finally catches up on this lesson. - 📚 Available for both Map and WeakMap, benefiting caching/memoization scenarios (where WeakMap keys are objects) as well.
- 🎯 Two methods, each with a role:
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 145 | 144 | 26.2 | 26+ | 2.6.7 | ✅ |
- Author: Richard Gibson
- Champion: Richard Gibson
- TC39 Meeting Notes: November 2025
- Description: The reviver function of
JSON.parsegets a new third parameter to access the raw source text; accompanied by newJSON.rawJSON()/JSON.isRawJSON()to control serialization output. - Core Problem Solved: The classic pain point of JSON number precision loss: when a backend returns an int64 ID or a high-precision amount, a single pass through
JSON.parseturns it into a precision-losing Number.
// ❌ Before: Large integer ID is corrupted upon parsing
const txt = '{"id": 9007199254740993}';
JSON.parse(txt).id; // 9007199254740992 —— silently lost 1
// ✅ ES2026: The reviver gets the original text, converts to BigInt without going through Number
const obj = JSON.parse(txt, (key, val, { source }) =>
key === 'id' ? BigInt(source) : val
);
obj.id; // 9007199254740993n ✓
// Serialization side: rawJSON allows large integers to be output as-is
JSON.stringify({ id: JSON.rawJSON('9007199254740993') });
// '{"id":9007199254740993}'
- Why it matters:
- 💰 A must-have for finance/ID scenarios: Previously, you could only pre-process JSON text with regex or ask the backend to change int64 to a string. Now it's solved within the standard.
- 🔒 Only works for primitive values:
context.sourceis only provided when parsing number/string/boolean/null, not for objects and arrays (to avoid huge string interning). - 🧩 Paves the way for the Decimal proposal: Future JSON interop for decimal number types will rely on this as a foundation.
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 114 | 135 | 18.4 | 21 | 1.33 | ✅ |
- Author: Michael Ficarra
- Champion: Michael Ficarra
- TC39 Meeting Notes: November 2025
- Description: Adds a new static method
Iterator.concat(...items)that concatenates multiple iterable objects into a single iterator in order. - Core Problem Solved: When concatenating multiple data sources,
[...a, ...b]requires materializing all elements into an array first (which blows up on infinite sequences), and writing a generatoryield*by hand is too verbose.
// ❌ Before: Either materialize an array or hand-write a generator
const merged = [...map.keys(), ...someSet, 'tail']; // Fully expanded, memory waste
function* concat(...its) { // Or eight lines of boilerplate
for (const it of its) yield* it;
}
// ✅ ES2026: Lazy concatenation, and you can continue chaining
const merged = Iterator.concat(map.keys(), someSet, ['tail']);
for (const x of merged.filter(v => v !== null).take(10)) {
// Lazy evaluation: won't pre-expand any data source
}
- Why it matters:
- 🔗 A direct complement to ES2025 Iterator Helpers: The returned object is also an Iterator Helper object, so you can chain
map/filter/takefreely. - ♾️ Infinite sequence friendly: Lazy consumption, concatenating generators is no problem.
- 🚧 Strictness: Arguments must be iterable objects; passing a primitive directly throws a TypeError, avoiding silent bugs.
- 🔗 A direct complement to ES2025 Iterator Helpers: The returned object is also an Iterator Helper object, so you can chain
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 146 | 147 | 26.4 | 26+ | 2.7.2 | ✅ |
- Author: Kevin Gibbons
- Champion: Kevin Gibbons
- TC39 Meeting Notes: July 2025
- Description: Adds Base64 / Hex encoding and decoding methods to Uint8Array: static
fromBase64/fromHex, instancetoBase64/toHex, andsetFromBase64/setFromHexfor streaming scenarios. - Core Problem Solved: JavaScript astonishingly never had a standard "binary ↔ Base64" API.
atob/btoaoperate on the ancient concept of "binary strings," don't support url-safe alphabets, and require manual byte-by-byte conversion for real binary data.
// ❌ Before: atob requires a three-step workaround
const binStr = atob('SGVsbG8gV29ybGQ=');
const bytes = Uint8Array.from(binStr, (c) => c.charCodeAt(0));
// ✅ ES2026: Direct
const bytes = Uint8Array.fromBase64('SGVsbG8gV29ybGQ=');
new TextDecoder().decode(bytes); // 'Hello World'
// url-safe, no padding, Hex are all natively supported
bytes.toBase64({ alphabet: 'base64url', omitPadding: true });
Uint8Array.fromHex('deadbeef'); // Uint8Array [222, 173, 190, 239]
// Streaming decode: only consumes complete 4-character blocks, leaving the rest for the next round
const buf = new Uint8Array(1024);
const { read, written } = buf.setFromBase64(chunk, {
lastChunkHandling: 'stop-before-partial',
});
- Why it matters:
- 🔐 High-frequency for WebCrypto / JWT / image dataURL scenarios: Encoding/decoding keys, signatures, and binary attachments no longer depends on third-party libraries or hacks.
- ⚡ Native engine implementation: An order of magnitude faster than hand-written JS. The
setFrom*series also supports buffer reuse and backpressure. - 🎛️ Full set of options:
alphabet(base64/base64url),lastChunkHandling(loose/strict/stop-before-partial),omitPadding.
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 140 | 133 | 18.2 | 25 | 2.5.0 | ✅ |
- Author: Kevin Gibbons
- Champion: Kevin Gibbons
- TC39 Meeting Notes: July 2025
- Description: Sums an iterable of Numbers as if calculated with infinite precision, rounding only once at the end.
- Core Problem Solved: A naive
reduce((a, b) => a + b)rounds to float64 at every step, accumulating errors. Small numbers can be completely "swallowed" when magnitudes differ greatly.
const xs = [1e20, 0.1, -1e20];
// ❌ Before: 0.1 is swallowed by 1e20
xs.reduce((a, b) => a + b, 0); // 0
// ✅ ES2026: The mathematically correct answer
Math.sumPrecise(xs); // 0.1
// Two details easy to stumble on
Math.sumPrecise([]); // -0 (not 0)
Math.sumPrecise([1, '2']); // TypeError! Rejects implicit conversion
- Why it matters:
- 📊 Correctness guarantee for statistics/scientific computing/financial summaries: The implementation typically uses the Shewchuk algorithm. Similar community libraries (like Python's
math.fsum) have long validated the need. - 🚫 Deliberately strict API: Only accepts an iterable (not rest parameters), throws an error immediately on non-Number values, exposing errors at development time.
- 📊 Correctness guarantee for statistics/scientific computing/financial summaries: The implementation typically uses the Shewchuk algorithm. Similar community libraries (like Python's
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 147 | 137 | 26.2 | ❌ Not yet supported | 2.7.8 | ✅ |
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: May 2025
- Description: Reliably determines if a value is a genuine Error object via a brand check on the internal slot
[[ErrorData]]. - Core Problem Solved:
instanceof Errorfails in cross-realm scenarios (iframes, Worker messages, Node'svmmodule) because different realms have their ownError.prototype. Duck-typing checks can be easily fooled by forged objects.
// ❌ Before: Can't recognize an Error from another realm
const vm = require('node:vm');
const err = vm.runInNewContext('new Error("boom")');
err instanceof Error; // false —— prototype chain comes from another realm
// ✅ ES2026: Brand check, doesn't look at the prototype chain
Error.isError(err); // true
Error.isError({ name: 'Error', message: '', stack: '' }); // false, forgery-proof
Error.isError(new DOMException('x')); // true, DOMException also counts
- Why it matters:
- 🛡️ A must-have for library authors: Error reporting SDKs, serialization libraries, and assertion libraries finally have a reliable way to discriminate, matching
Array.isArray()for language consistency. - 🕰️ An old proposal overturned: It can be traced back to 2016. After being shelved for years, it was revived and pushed by Jordan Harband in 2024, finalizing at lightning speed within a year.
- 🛡️ A must-have for library authors: Error reporting SDKs, serialization libraries, and assertion libraries finally have a reliable way to discriminate, matching
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 134 | 138 | 18.4 | 24.3 | 2.2 | ✅ |
- Author: J.S. Choi
- Champion: J.S. Choi
- TC39 Meeting Notes: May 2025
- Description: The async version of
Array.from: consumes an async iterable (or a sync iterable of Promises), returns aPromise<Array>, and supports an optional (potentially async)mapFn. - Core Problem Solved: The expression-ification of the
for await...of+ manualpushboilerplate.
// ❌ Before: Four lines of boilerplate
const chunks = [];
for await (const chunk of stream) {
chunks.push(chunk);
}
// ✅ ES2026: One line
const chunks = await Array.fromAsync(stream);
// Collect paginated data with an async generator
async function* paginate(url) {
while (url) {
const r = await (await fetch(url)).json();
yield* r.items;
url = r.next;
}
}
const ids = await Array.fromAsync(paginate('/api/list'), (item) => item.id);
- Why it matters:
- 🌊 Serial consumption, respects backpressure: Semantically complementary to
Promise.all(which concurrently awaits an existing set of Promises), it won't overwhelm a producer when processing a stream. - 🐢 Waited at Stage 3 for nearly three years (reached Stage 3 in 2022-09, waiting for a second engine implementation). This is why it's actually the earliest browser-landed feature among the ES2026 seven: Safari 16.4 / Firefox 115 / Chrome 121 have been usable for a while.
- 🌊 Serial consumption, respects backpressure: Semantically complementary to
- Compatibility:
| Chrome | Firefox | Safari | Node.js | Deno | Bun |
|---|---|---|---|---|---|
| 121 | 115 | 16.4 | 22 | 1.38 | ✅ |
ES2025
Officially released on June 25, 2025, including the following new features:
- Authors: Domenic Denicola, Benjamin Gruenbaum, Jordan Harband
- Champions: Jordan Harband, Kevin Gibbons
- TC39 Meeting Notes: February 2025
- Description: Provides a static method to escape special regex characters in a string.
- Core Problem Solved: Safely using a plain string in a regular expression, preventing special characters from being misinterpreted.
// ❌ Before: Manual escaping was error-prone
const userInput = "price: $9.99";
const pattern = new RegExp(userInput); // 💥 $ and . will be treated as special characters
// ✅ ES2025: Standardized safe escaping
const escaped = RegExp.escape("price: $9.99");
// Returns: "price:\\x20\\$9\\.99"
const safePattern = new RegExp(escaped); // Correctly matches the literal string
Why it matters:
- 🔥 Extremely high community demand: The
escape-string-regexppackage on npm has over 100 million weekly downloads. - 🌍 International standard alignment: JavaScript finally has a feature that other languages have had for 20 years (Python
re.escape(), RubyRegexp.escape(), etc.). - 🛡️ Security guarantee: Prevents regex injection vulnerabilities caused by user input.
- 🔥 Extremely high community demand: The
Typical Application Scenarios:
// Search highlighting function highlight(text, term) { const pattern = new RegExp(`(${RegExp.escape(term)})`, 'gi'); return text.replace(pattern, '<mark>$1</mark>'); } // Dynamic replacement function replaceAll(text, find, replace) { const pattern = new RegExp(RegExp.escape(find), 'g'); return text.replace(pattern, replace); }Proposal Journey: This proposal was discussed starting in 2015, took 10 years, and finally reached Stage 4 in February 2025, becoming part of ES2025. The proposal repository was archived on February 18, 2025, marking the feature's formal finalization.
Author: Leo Balter
Champions: Leo Balter, Kevin Gibbons
TC39 Meeting Notes: February 2025
Description: Adds the Float16Array typed array, related DataView methods, and Math.f16round.
Core Value: Introduces 16-bit floating-point (half-precision) support to JavaScript, primarily serving AI/machine learning and graphics processing scenarios.
// New TypedArray type const float16 = new Float16Array([1.0, 2.5, 3.14159]); // Memory efficiency comparison const float32Array = new Float32Array(1000); // 4000 bytes const float16Array = new Float16Array(1000); // 2000 bytes (50% savings) // Math.f16round - round to 16-bit precision Math.f16round(3.14159); // 3.140625 Math.f16round(65504); // 65504 (max value) Math.f16round(65505); // Infinity // DataView support const buffer = new ArrayBuffer(2); const view = new DataView(buffer); view.setFloat16(0, 3.14); console.log(view.getFloat16(0)); // 3.140625Why it matters:
- 🤖 AI/ML Optimization: Most neural network inference can use float16 without losing accuracy, halving memory usage.
- 🎮 Graphics Processing: WebGL and WebGPU natively support float16; now JavaScript can operate on it directly.
- 💾 Memory Efficiency: Significantly reduces memory footprint when processing large-scale numerical data.
Practical Application Scenarios:
// AI model weight storage async function loadModelWeights(url) { const response = await fetch(url); const buffer = await response.arrayBuffer(); // Use Float16Array to load model weights, saving 50% memory return new Float16Array(buffer); } // Color values in image processing function processHDRImage(pixelData) { // HDR images use float16 to store color values const float16Pixels = new Float16Array(pixelData); // Process pixel data... } // Interacting with WebGPU const gpuBuffer = device.createBuffer({ size: float16Array.byteLength, usage: GPUBufferUsage.VERTEX, mappedAtCreation: true }); new Float16Array(gpuBuffer.getMappedRange()).set(float16Array);Proposal Journey: The Float16Array proposal responded to the Web platform's demand for efficient numerical computation, especially with the rise of WebGPU and AI applications in the browser. The proposal repository was archived on May 24, 2025, marking the feature's formal finalization.
Author: Jordan Harband
Champion: Jordan Harband
TC39 Meeting Notes: October 2024
Description: Provides a unified way to handle both synchronous and asynchronous functions.
Core Problem Solved: Elegantly wraps any function (sync or async) into a Promise while maintaining the performance advantage of synchronous execution.
// ❌ The previous dilemma // Method 1: Simple but inefficient (forces async) Promise.resolve().then(() => fn()); // fn is deferred to the next microtask // Method 2: Efficient but verbose new Promise(resolve => resolve(fn())); // Sync execution but clumsy syntax // ✅ ES2025: Concise and efficient Promise.try(() => fn()); // Sync execution, automatically catches errorsWhy it matters:
- ⚡ Performance optimization: The function can execute synchronously, avoiding unnecessary async overhead.
- 🛡️ Unified error handling: Whether the function throws an exception or not, it can be handled with
.catch(). - 📦 Community validated: The
p-trynpm package has 44 million weekly downloads and 8.9 billion total downloads.
Practical Application Scenarios:
// 1. Unsure if a function is async function processData(data) { return Promise.try(() => { // Could be sync validation or an async API call return validateOrFetch(data); }).then(result => { console.log('Processing successful:', result); }).catch(error => { console.error('Processing failed:', error); }); } // 2. Error handling for initialization flow Promise.try(() => { // Initialization code that might throw const config = JSON.parse(configString); return initializeApp(config); }).catch(error => { console.error('Initialization failed:', error); showErrorUI(); }); // 3. Pairing with async/await async function main() { const result = await Promise.try(() => { // Sync code can also enjoy async/await error handling return complexCalculation(); }); }Standardization Process: This proposal has reached Stage 4, and the repository was archived on October 9, 2024. As part of ES2025, it fills a long-standing gap in the Promise API, making JavaScript's async programming model more complete and consistent.
Ecosystem Impact: Major Promise libraries like Bluebird, Q, and when have long provided similar functionality. ES2025's standardization means developers can finally use this high-frequency feature in the native environment, reducing reliance on third-party libraries.
Author: Gus Caplan
Champions: Michael Ficarra, Jonathan Keslin, Kevin Gibbons
TC39 Meeting Notes: October 2024
Description: Adds helper methods like map, filter, take, drop, etc., to iterators.
Core Problem Solved: Provides array-like chainable operation methods for iterators, making them as easy to use as arrays.
// ❌ Before: Iterators lacked helper methods function* naturals() { let i = 0; while (true) yield i++; } // Needed manual conversion to array to use map/filter const squares = Array.from(naturals()) .slice(0, 10) // Dangerous! naturals() is infinite .map(x => x * x); // ✅ ES2025: Iterators natively support chainable operations const squares = naturals() .take(10) // Safe! Lazy evaluation .map(x => x * x) .toArray(); // [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]Core Feature Overview:
- Transformation methods:
map(),filter(),flatMap() - Truncation methods:
take(),drop() - Aggregation methods:
reduce(),toArray() - Predicate methods:
some(),every(),find() - Side effects:
forEach() - Static method:
Iterator.from()
- Transformation methods:
Why it matters:
- 🚀 Lazy evaluation: All methods are lazy, consuming the iterator only when needed.
- ♾️ Infinite sequence support: Can safely handle infinite iterators.
- 🔗 Chainable calls: Provides a fluent functional programming experience.
- 📚 Cross-language consistency: Aligns with iterator APIs in Rust, Python, C#, etc.
Standardization Process: This proposal has reached Stage 4, and the repository was archived on October 8, 2024. This marks JavaScript finally having a modern iterator API, closing the gap in iterator processing capabilities with languages like Rust and Python.
Ecosystem Impact: Node.js's Readable streams have already implemented all features of this proposal. The functionality of many popular libraries like lodash and itertools can now be replaced with native APIs, reducing project dependencies.
Authors: Myles Borins, Sven Sauleau, Dan Clark, Daniel Ehrenberg
Champions: Myles Borins, Sven Sauleau, Dan Clark, Daniel Ehrenberg
TC39 Meeting Notes: October 2024
Description: Allows directly importing JSON files as modules.
Core Problem Solved: Safely and standardly importing JSON data, preventing code injection attacks caused by MIME type confusion.
// ❌ The previous dilemma // Method 1: Use fetch (runtime loading, requires extra parsing) const response = await fetch('./config.json'); const data = await response.json(); // Method 2: Rely on build tools (non-standard, behavior varies by tool) import data from './config.json'; // Requires webpack/rollup etc. support // Method 3: Dynamic import (not intuitive enough) const module = await import('./config.json'); // ✅ ES2025: Standard and safe import data from './config.json' with { type: 'json' };Why it matters:
- 🔒 Security guarantee: The explicit
type: "json"declaration prevents the server from returning executable code. - 📦 Zero dependencies: No longer need build tools or polyfills to import JSON.
- 🌍 Cross-platform consistency: Behavior is unified across browsers, Node.js, Deno, and other environments.
- ⚡ Compile-time optimization: Static imports allow for better tree shaking and optimization.
- 🔒 Security guarantee: The explicit
Standardization Process:
- July 2020: Separated from the Import Attributes proposal.
- October 8, 2024: Entered Stage 4 together with Import Attributes.
- Initially implemented by Microsoft for V8/Chromium.
- Security considerations raised by Apple (Ryosuke Niwa) and Mozilla (Anne van Kesteren) led to the current design.
Ecosystem Impact:
- Build tool simplification: Webpack, Rollup, Vite, etc., can use standard syntax.
- Type system support: TypeScript can provide better type inference.
- Toolchain unification: Tools like ESLint and Prettier can recognize standard syntax.
- Performance optimization: Allows for better static analysis and tree shaking.
Authors: Myles Borins, Sven Sauleau, Dan Clark, Daniel Ehrenberg
Champions: Sven Sauleau, Dan Clark, Daniel Ehrenberg, Nicolò Ribaudo
TC39 Meeting Notes: October 2024
Description: Adds inline syntax to module import statements to pass more information beyond the module specifier, providing infrastructure for non-JavaScript module types like JSON, CSS, and HTML.
Core Problem Solved: Provides a secure, standardized way to specify module type and other metadata, preventing MIME type confusion attacks.
// ❌ The previous dilemma // Method 1: Rely on file extension (unreliable) import data from './config.json'; // Extension doesn't guarantee content type // Method 2: Rely on MIME type (can be tampered with) import styles from './styles.css'; // Server might return JavaScript // Method 3: Use URL scheme (complex and non-standard) import data from 'json:./config.json'; // Pseudo-protocol scheme // ✅ ES2025: Explicit and safe import data from './config.json' with { type: 'json' }; import styles from './styles.css' with { type: 'css' };Why it matters:
- 🔒 Security foundation: Provides a secure import mechanism for all non-JavaScript modules.
- 🏗️ Extensible architecture: Supports not just
type, but also other extensible attributes. - 🌐 Cross-environment consistency: Unified behavior across browsers, Node.js, and Deno.
- ⚙️ Backward compatibility: Smooth transition from
asserttowith.
Standardization Process:
- 2019-12: Stage 1 as "module attributes".
- 2020-06: Stage 2, restricting attributes from being used as cache keys.
- 2020-09: Stage 3, renamed "import assertions", using the
assertkeyword. - 2021-05 to 2022-02: Implemented and shipped in Chrome, Node.js, Deno.
- 2023-01: Demoted back to Stage 2 due to incompatibility with HTML semantics.
- 2023-03: Renamed "import attributes", changed back to
withkeyword, returned to Stage 3. - 2024-10: Entered Stage 4.
Ecosystem Impact:
- Unified module system: Provides a unified import mechanism for various resource types on the Web platform.
- Security enhancement: Explicit type declaration prevents content injection attacks.
- Toolchain standardization: Build tools can optimize based on standard syntax.
- Future extensibility: Reserves space for more module types and attributes.
Author: Ron Buckton
Champion: Ron Buckton
TC39 Meeting Notes: October 2024
Description: Allows dynamically enabling or disabling modifier flags within a regular expression, providing finer-grained pattern control.
Core Problem Solved: Applying different flag settings to different parts of a regular expression without splitting it into multiple regexes or using complex workarounds.
// ❌ The previous dilemma // Method 1: Use multiple regexes const re1 = /[a-z]/i; // Case-insensitive const re2 = /[a-z]/; // Case-sensitive // Need to combine them, complex logic // Method 2: Dynamically build regex const pattern = '[a-z]'; const re = new RegExp(pattern, someCondition ? 'i' : ''); // Method 3: Complex character classes /[a-zA-Z][a-z]/ // First char case-insensitive, second case-sensitive // ✅ ES2025: Inline modifiers /^[a-z](?-i:[a-z])$/i; // The second part disables the i flag /^(?i:[a-z])[a-z]$/; // The first part enables the i flagWhy it matters:
- 🎯 Precise control: Apply different flags to specific parts of a regex.
- 📝 Configuration-friendly: Suitable for scenarios like JSON configs, syntax highlighters where code execution isn't possible.
- 🔧 Reduces complexity: Avoids splitting regexes or using complex workarounds.
- 🌍 Industry standard: Consistent with mainstream regex engines like Perl, PCRE, .NET.
Standardization Process:
- 2021-10-27: Proposal for Stage 1 (passed).
- 2021-12-15: Entered Stage 2 (limited to self-contained form
(?i:...)). - 2022-06-07: Entered Stage 3.
- 2024-10: Entered Stage 4.
Ecosystem Impact:
- Parser enhancement: Syntax highlighters and code analysis tools can process text more precisely.
- Configuration simplification: TextMate grammars and JSON configs can use more powerful patterns.
- Regex readability: Reduces the need to split or dynamically build regexes.
- Cross-language consistency: Regex syntax is closer to languages like Perl, Python, .NET.
Authors: Michał Wadas, Sathya Gunasekaran, Kevin Gibbons
Champion: Kevin Gibbons
TC39 Meeting Notes: April 2024
Description: Adds set operation methods like union, intersection, difference, etc., to JavaScript's built-in
Setclass.Core Problem Solved: Native Set lacked basic set operation methods, forcing developers to write complex logic or rely on third-party libraries.
// ❌ The previous dilemma // Method 1: Hand-write set operations (tedious and error-prone) function intersection(setA, setB) { const result = new Set(); for (const elem of setA) { if (setB.has(elem)) { result.add(elem); } } return result; } // Method 2: Use spread operator and filter (poor performance) const intersection = new Set([...setA].filter(x => setB.has(x))); const union = new Set([...setA, ...setB]); // Method 3: Rely on third-party libraries import { intersection, union } from 'lodash'; // ✅ ES2025: Native support setA.intersection(setB); setA.union(setB); setA.difference(setB);Why it matters:
- 🧮 Mathematical completeness: Set finally has complete set operation capabilities.
- ⚡ Performance optimization: Native implementation is more efficient than JavaScript implementation.
- 🎯 Code conciseness: No more tedious loops and temporary arrays.
- 🌍 Language alignment: Consistent with Python, Ruby, Swift, etc.
Standardization Process:
- 2018-05: First discussed at a TC39 meeting.
- 2019-01: Continued discussion on details.
- 2022-03: Discussed how to extend built-in types.
- 2023-07: Key design decisions.
- 2024-02: Nearing completion.
- 2024-04: Entered Stage 4.
Ecosystem Impact:
- Reduced dependencies: No longer need set operations from libraries like lodash, ramda.
- Performance boost: Native C++ implementation is faster than JavaScript implementation.
- Code readability: Set operation semantics are clearer.
Author: Kevin Gibbons
Champion: Kevin Gibbons
TC39 Meeting Notes: April 2024
Description: Allows reusing the same capture group name in different branches of a regular expression.
Core Problem Solved: When matching text in multiple formats, you couldn't use the same group name for semantically identical parts, leading to code redundancy and complex processing.
// ❌ The previous dilemma // Method 1: Use different group names (semantically unclear) const dateRegex = /(?<year1>[0-9]{4})-[0-9]{2}|[0-9]{2}-(?<year2>[0-9]{4})/; const match = str.match(dateRegex); const year = match.groups.year1 || match.groups.year2; // Need manual merging // Method 2: Use numbered capture groups (poor readability) const dateRegex = /([0-9]{4})-[0-9]{2}|[0-9]{2}-([0-9]{4})/; const match = str.match(dateRegex); const year = match[1] || match[2]; // Method 3: Use multiple regexes (poor performance) const regex1 = /(?<year>[0-9]{4})-[0-9]{2}/; const regex2 = /[0-9]{2}-(?<year>[0-9]{4})/; const match = str.match(regex1) || str.match(regex2); // ✅ ES2025: Allows duplicate naming const dateRegex = /(?<year>[0-9]{4})-[0-9]{2}|[0-9]{2}-(?<year>[0-9]{4})/; const match = str.match(dateRegex); const year = match.groups.year; // Direct access, automatically picks the matched branchWhy it matters:
- 🎯 Semantic consistency: Use the same name for capture groups with the same meaning.
- 📝 Code conciseness: No need to manually merge results from multiple groups.
- 🔄 Format flexibility: Easily handle multiple input formats.
- 🌍 Language alignment: Consistent with .NET, Python, Perl, etc.
Standardization Process Update:
- 2022-07: Reached Stage 3.
- 2024-04: Entered Stage 4.
- Author: Shu-yu Guo (V8)
- TC39 Meeting Notes: February 2025
- Description: A purely specification-level fix, so obscure that almost no annual ES2025 recap on the market mentions it. Variables introduced in the global scope via direct
eval("var x")were already deletable (configurable). After the fix, they are also allowed to be redeclared by subsequentlet/const, and the[[VarNames]]mechanism in the global environment record, which specifically tracked such variables, was removed, reducing engine implementation complexity. It has almost no impact on daily business code and is the kind of spec corner knowledge that only "the most comprehensive guide on the web" would include to round out all 10 proposals 😄.
ES2024
- Well-Formed Unicode Strings
- Authors: Guy Bedford, Bradley Farias
- Champions: Guy Bedford, Bradley Farias, Michael Ficarra
- TC39 Meeting Notes: May 2023
- Release Year: 2024
Atomics.waitAsync- Author: Lars Hansen
- Champions: Shu-yu Guo, Lars Hansen
- TC39 Meeting Notes: May 2023
- Release Year: 2024
- RegExp v flag with set notation + properties of strings
- Authors: Markus Scherer, Mathias Bynens
- Champion: Mathias Bynens
- TC39 Meeting Notes: May 2023
- Release Year: 2024
- Resizable and growable ArrayBuffers
- Author: Shu-yu Guo
- Champion: Shu-yu Guo
- TC39 Meeting Notes: September 2023
- Release Year: 2024
- Array Grouping
- Author: Justin Ridgewell
- Champions: Justin Ridgewell, Jordan Harband
- TC39 Meeting Notes: November 2023
- Release Year: 2024
Promise.withResolvers- Author: Peter Klecha
- Champion: Peter Klecha
- TC39 Meeting Notes: November 2023
- Release Year: 2024
- ArrayBuffer transfer
- Authors: Shu-yu Guo, Jordan Harband, Yagiz Nizipli
- Champions: Shu-yu Guo, Jordan Harband, Yagiz Nizipli
- TC39 Meeting Notes: February 2024
- Release Year: 2024
ES2023
- Array find from last
- Author: Wenlu Wang
- Champions: Wenlu Wang, Daniel Rosenwasser
- TC39 Meeting Notes: June 2022
- Release Year: 2023
- Hashbang Grammar
- Author: Bradley Farias
- Champion: Bradley Farias
- TC39 Meeting Notes: July 2022
- Release Year: 2023
- Symbols as WeakMap keys
- Authors: Daniel Ehrenberg, Richard Button, Robin Ricard, Leo Balter, Rick Waldron, Caridy Patiño
- Champions: Daniel Ehrenberg, Richard Button, Robin Ricard, Leo Balter, Rick Waldron, Caridy Patiño
- TC39 Meeting Notes: January 2023
- Release Year: 2023
- Change Array by Copy
- Authors: Ashley Claymore, Robin Ricard
- Champions: Ashley Claymore, Robin Ricard
- TC39 Meeting Notes: March 2022
- Release Year: 2023
ES2022
- Class Fields ( Private instance methods and accessors , Class Public Instance Fields & Private Instance Fields , Static class fields and private static methods )
- Author: Daniel Ehrenberg
- Champions: Daniel Ehrenberg, Kevin Gibbons
- TC39 Meeting Notes: April 2021
- Release Year: 2022
- RegExp Match Indices
- Author: Ron Buckton
- Champion: Ron Buckton
- TC39 Meeting Notes: May 2021
- Release Year: 2022
- Top-level
await- Authors: Myles Borins, Yulia Startsev, Daniel Ehrenberg, Guy Bedford, Ms2ger
- Champions: Myles Borins, Yulia Startsev
- TC39 Meeting Notes: May 2021
- Release Year: 2022
- Ergonomic brand checks for Private Fields
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: July 2021
- Release Year: 2022
.at()- Authors: Shu-yu Guo, Tab Atkins
- Champions: Shu-yu Guo, Tab Atkins
- TC39 Meeting Notes: August 2021
- Release Year: 2022
- Accessible
Object.prototype.hasOwnProperty- Author: Jamie Kyle
- Champions: Tierney Cyren, Jamie Kyle
- TC39 Meeting Notes: August 2021
- Release Year: 2022
- Class Static Block
- Author: Ron Buckton
- Champion: Ron Buckton
- TC39 Meeting Notes: August 2021
- Release Year: 2022
- Error Cause
- Author: Chengzhong Wu
- Champions: Chengzhong Wu, Hemanth HM
- TC39 Meeting Notes: October 2021
- Release Year: 2022
ES2021
- String.prototype.replaceAll
- Authors: Peter Marshall, Jakob Gruber, Mathias Bynens
- Champion: Mathias Bynens
- TC39 Meeting Notes: June 2020
- Release Year: 2021
- Promise.any
- Authors: Mathias Bynens, Kevin Gibbons, Sergey Rubanov
- Champion: Mathias Bynens
- TC39 Meeting Notes: July 2020
- Release Year: 2021
- WeakRefs
- Authors: Dean Tribble, Sathya Gunasekaran
- Champions: Dean Tribble, Mark Miller, Till Schneidereit, Sathya Gunasekaran, Daniel Ehrenberg
- TC39 Meeting Notes: July 2020
- Release Year: 2021
- Logical Assignment Operators
- Author: Justin Ridgewell
- Champions: Justin Ridgewell, Hemanth HM
- TC39 Meeting Notes: July 2020
- Release Year: 2021
- Numeric separators
- Authors: Sam Goto, Rick Waldron
- Champions: Sam Goto, Rick Waldron, Leo Balter
- TC39 Meeting Notes: July 2020
- Release Year: 2021
ES2020
- String.prototype.matchAll
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: March 2019
- Release Year: 2020
- import()
- Author: Domenic Denicola
- Champion: Domenic Denicola
- TC39 Meeting Notes: June 2019
- Release Year: 2020
- BigInt
- Author: Daniel Ehrenberg
- Champion: Daniel Ehrenberg
- TC39 Meeting Notes: June 2019
- Release Year: 2020
- Promise.allSettled
- Authors: Jason Williams, Robert Pamely, Mathias Bynens
- Champion: Mathias Bynens
- TC39 Meeting Notes: July 2019
- Release Year: 2020
- globalThis
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: October 2019
- Release Year: 2020
- for-in mechanics
- Author: Kevin Gibbons
- Champion: Kevin Gibbons
- TC39 Meeting Notes: December 2019
- Release Year: 2020
- Optional Chaining
- Authors: Gabriel Isenberg, Claude Pache, Dustin Savery
- Champions: Gabriel Isenberg, Dustin Savery, Justin Ridgewell, Daniel Rosenwasser
- TC39 Meeting Notes: December 2019
- Release Year: 2020
- Nullish coalescing Operator
- Author: Gabriel Isenberg
- Champions: Gabriel Isenberg, Justin Ridgewell, Daniel Rosenwasser
- TC39 Meeting Notes: December 2019
- Release Year: 2020
- import.meta
- Author: Domenic Denicola
- Champion: Gus Caplan
- TC39 Meeting Notes: March 2020
- Release Year: 2020
ES2019
- Optional catch binding
- Author: Michael Ficarra
- Champion: Michael Ficarra
- TC39 Meeting Notes: May 2018
- Release Year: 2019
- JSON superset
- Author: Richard Gibson
- Champions: Mark Miller, Mathias Bynens
- TC39 Meeting Notes: May 2018
- Release Year: 2019
- Symbol.prototype.description
- Author: Michael Ficarra
- Champion: Michael Ficarra
- TC39 Meeting Notes: November 2018
- Release Year: 2019
- Function.prototype.toString revision
- Author: Michael Ficarra
- Champion: Michael Ficarra
- TC39 Meeting Notes: November 2018
- Release Year: 2019
- Object.fromEntries
- Author: Darien Maillet Valentine
- Champions: Jordan Harband, Kevin Gibbons
- TC39 Meeting Notes: January 2019
- Release Year: 2019
- Well-formed JSON.stringify
- Author: Richard Gibson
- Champion: Mathias Bynens
- TC39 Meeting Notes: January 2019
- Release Year: 2019
- String.prototype.{trimStart,trimEnd}
- Author: Sebastian Markbåge
- Champions: Sebastian Markbåge, Mathias Bynens
- TC39 Meeting Notes: January 2019
- Release Year: 2019
- Array.prototype.{flat,flatMap}
- Authors: Brian Terlson, Michael Ficarra, Mathias Bynens
- Champions: Brian Terlson, Michael Ficarra
- TC39 Meeting Notes: January 2019
- Release Year: 2019
ES2018
- Lifting template literal restriction
- Author: Tim Disney
- Champion: Tim Disney
- TC39 Meeting Notes: March 2017
- Release Year: 2018
- s (dotAll) flag for regular expressions
- Author: Mathias Bynens
- Champions: Brian Terlson, Mathias Bynens
- TC39 Meeting Notes: November 2017
- Release Year: 2018
- RegExp named capture groups
- Authors: Gorkem Yakin, Daniel Ehrenberg
- Champions: Daniel Ehrenberg, Brian Terlson, Mathias Bynens
- TC39 Meeting Notes: November 2017
- Release Year: 2018
- Rest/Spread Properties
- Author: Sebastian Markbåge
- Champion: Sebastian Markbåge
- TC39 Meeting Notes: January 2018
- Release Year: 2018
- RegExp Lookbehind Assertions
- Authors: Gorkem Yakin, Nozomu Katō, Daniel Ehrenberg
- Champions: Daniel Ehrenberg, Mathias Bynens
- TC39 Meeting Notes: January 2018
- Release Year: 2018
- RegExp Unicode Property Escapes
- Author: Mathias Bynens
- Champions: Brian Terlson, Daniel Ehrenberg, Mathias Bynens
- TC39 Meeting Notes: January 2018
- Release Year: 2018
- Promise.prototype.finally
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: January 2018
- Release Year: 2018
- Asynchronous Iteration
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: January 2018
- Release Year: 2018
ES2017
- Object.values/Object.entries
- Author: Jordan Harband
- Champion: Jordan Harband
- TC39 Meeting Notes: March 2016
- Release Year: 2017
- String padding
- Author: Jordan Harband
- Champions: Jordan Harband, Rick Waldron
- TC39 Meeting Notes: May 2016
- Release Year: 2017
- Object.getOwnPropertyDescriptors
- Authors: Jordan Harband, Andrea Giammarchi
- Champions: Jordan Harband, Andrea Giammarchi
- TC39 Meeting Notes: May 2016
- Release Year: 2017
- Trailing commas in function parameter lists and calls
- Author: Jeff Morrison
- Champion: Jeff Morrison
- TC39 Meeting Notes: July 2016
- Release Year: 2017
- Async functions
- Author: Brian Terlson
- Champion: Brian Terlson
- TC39 Meeting Notes: July 2016
- Release Year: 2017
- Shared memory and atomics
- Author: Lars T Hansen
- Champion: Lars T Hansen
- TC39 Meeting Notes: January 2017
- Release Year: 2017
ES2016
- Array.prototype.includes
- Author: Domenic Denicola
- Champions: Domenic Denicola, Rick Waldron
- TC39 Meeting Notes: November 2015
- Release Year: 2016
- Exponentiation operator
- Author: Rick Waldron
- Champion: Rick Waldron
- TC39 Meeting Notes: January 2016
- Release Year: 2016
ES2015 (ES6 Inaugural Year Special)
Tracing all the way back here, we finally arrive at the starting point of the legend. ES2015 is unlike all subsequent versions: it was the last "bundled" major release, predating TC39's staged process. The finished-proposals list only started recording from ES2016 onwards, and its twenty-plus features have no corresponding proposal entries to trace. Most "features through the years" recaps on the market therefore start from 2016. This article fills in that missing piece.
A one-sentence background: The overly ambitious ES4 was declared dead in 2008. The committee pivoted to a pragmatic route codenamed Harmony, accumulated work for six or seven years, and released the 6th Edition in one go on June 17, 2015. The "one version per year" rhythm started right here: holding back for six years produced a behemoth, stretching the release process to its limit. TC39 learned its lesson and switched to small, fast steps, which is why we have the 11 bars in the chart above.
| Category | Features |
|---|---|
| Syntax | let / const, Arrow functions, Class, Template strings, Destructuring assignment, Default parameters, rest/spread, Enhanced object literals |
| Modules | import / export (ES Modules) |
| Async | Promise, Generator (function* / yield) |
| Collections & Iteration | Map / Set / WeakMap / WeakSet, Iterator protocol, for...of |
| Metaprogramming | Symbol, Proxy / Reflect |
| Standard Library | Object.assign, Array.from / Array.of / find, String.prototype.includes, Binary/Octal literals, and a large batch of others |
| Other | Tail call optimization (written into the spec, but to this day only Safari's JavaScriptCore has truly implemented it) |
How heavy is this edition? The following 11 years totaled 78 entries, but ES2015 alone reshaped half the face of the language. So when a job description says "proficient in ES6," it never refers to just one year, but to the entire modern JavaScript era that started from here.
Expected New Features for ES2027 (Preview)
The ES2026 candidate specification was finalized at the March 2026 meeting, but TC39 was firing on all cylinders in the first half of 2026. 4 proposals reached Stage 4 after the cutoff line, and by convention, they will ship with ES2027. Moreover, this batch is heavier than the ES2026 main event itself:
- Temporal —— A nine-year marathon, finally crossing the finish line 🏁
- Stage 4 Date: March 11, 2026 (Released after two major browsers achieved over 99.5% pass rates on approximately 5000 test262 conformance tests)
- Description: A brand-new date/time API, the "official successor" to
Date: immutable objects, explicit time zones and calendars, nanosecond precision, a full suite of types likeTemporal.PlainDate/ZonedDateTime/Duration. - Implementation Progress: Firefox 139 (May 2025) was the first globally to enable it by default; Chrome 144 (January 2026) followed, with V8's implementation reusing the Rust library temporal_rs; Safari, as of mid-2026, was still polishing it in Technology Preview; Node.js 26 / Deno 2.7 already support it.
- The proposal started in 2017, journeying from the "pain of moment.js" to a standard. In the year waiting for it to enter the ES2027 spec text, you can already use it directly in two major browsers.
const meeting = Temporal.ZonedDateTime.from(
'2026-07-12T10:00[Asia/Shanghai]'
);
meeting.add({ hours: 3 }).toString(); // Immutable, timezone-safe
Temporal.Now.plainDateISO().toString(); // '2026-07-12'
- Stage 4 Date: May 19, 2026 (Note: It remained at Stage 3 throughout 2025. Many articles prematurely counted it as part of ES2025/ES2026, which was incorrect.)
- Description:
using/await usingdeclarations +Symbol.dispose/Symbol.asyncDispose+DisposableStack/AsyncDisposableStack. Automatically releases resources (file handles, locks, streams) at the end of a scope. JavaScript's version of RAII / Python'swith. - Implementation Progress: Chrome 134 / Firefox 141 / Node 24 / Deno 2.2 already support it; Safari does not yet; TypeScript can transpile it since 5.2.
async function copy(src, dst) {
await using input = await fs.open(src);
await using output = await fs.open(dst, 'w');
// Both automatically call [Symbol.asyncDispose]() when leaving scope, even on exceptions
}
- Stage 4 Date: May 19, 2026
- Description: Walking multiple iterators in parallel:
Iterator.zip([a, b])yields tuples,zipKeyed({ x: a, y: b })yields named objects. Supportsshortest/longest/strictmodes for handling mismatched lengths. - Implementation Progress: Currently only Firefox 148 has a stable release; other engines are still on the way. It's the furthest from daily usability among the four.
- Stage 4 Date: May 19, 2026 (A normative change was passed in the same meeting: the iteration count parameter from the original design was removed. The final API is the parameterless
Atomics.pause(). Old documentation online showingpause(N)is now obsolete.) - Description: Issues a "microwait" hint to the CPU in spinlock scenarios (corresponding to the x86
PAUSEinstruction), reducing power consumption and bus contention during busy-waiting in multi-threaded contexts (SharedArrayBuffer + Worker). - Implementation Progress: Chrome 133 / Firefox 137 / Safari 18.4 / Node 24 already support it.
The four above, see you in 2027.
TC39 Proposal Dynamics Quick Look (H2 2025 → H1 2026)
Besides the finalized features, there was quite a bit of big TC39 news this year. Let's look at them one by one:
📉 Decorators Rarely Downgraded: On May 19, 2026, Decorators was demoted from Stage 3 back to Stage 2.7 (Decorator Metadata followed suit the same day). After reaching Stage 3 in March 2022 and waiting four years without any native browser implementation, engine vendors ultimately pushed for the downgrade citing "incomplete tests, specification issues, and implementation complexity." The meeting even discussed the possibility of a future demotion to Stage 2. The TS/Babel transpilation ecosystem works as usual, but "native decorators" are again nowhere in sight. This is also the most famous "reheat" since the establishment of Stage 2.7.
🪦 Records & Tuples Officially Withdrawn: The #{} / #[] immutable primitive value path was officially withdrawn in April 2025, officially marked as "succeeded by Composites" (Stage 1). The new path abandons new primitive types, instead using composite keys with regular object semantics to allow Map/Set to support value-based lookup.
🔥 The Iterator Full Suite Era: Following ES2025 Iterator Helpers, ES2026 Iterator.concat, and ES2027 Iterator.zip, Iterator Chunking (chunks(n) / windows(n), Stage 3), Iterator Includes (Stage 3, jumped from Stage 1 to 3 in about three months, the fastest-rising proposal of 2026), and Iterator Join (Stage 3) are queuing up. The May 2026 meeting also specifically discussed the iterator proposal roadmap.
📦 Module Harmony Family Advancing: import defer (Stage 3, TypeScript 5.9 already supports the syntax, V8 has implemented it behind a flag), Import Text / Import Bytes (importing text/binary resources, Stage 3 / 2.7 respectively), import.sync (Stage 2). ES modules are filling in the two puzzle pieces of "non-JS resource imports" and "fine-grained loading timing control."
Other Highlights: Immutable ArrayBuffer received "conditional Stage 3" (waiting for test262 to land); Error Stack Accessor was promoted to Stage 3 (error.stack is finally being standardized); RegExp Buffer Boundaries (\A \z), dormant for four and a half years, was revived and jumped two stages in one week to Stage 3; ShadowRealm is still stuck on host integration; AsyncContext remains at Stage 2, with browser vendors' concerns about performance overhead unresolved.
Conclusion
Give a man a fish, and you feed him for a day; teach a man to fish, and you feed him for a lifetime. I hope that by tracing things back to their roots, this article can lead you into the world of ECMAScript, making the mysterious ES codes no longer mysterious and new ES features no longer bewildering.
References
- Ecma International 131st General Assembly Approves ECMA-262 17th Edition (ES2026) Announcement
- ECMAScript® 2026 Language Specification
- TC39 Finished Proposals List (finished-proposals.md)
- InfoWorld: ECMAScript 2026 specification approved
- ES2026 spec release (tc39/ecma262)
- What's new in ECMAScript 2026 - pawelgrzybek
- Ecma Memento 2026 Yearbook (including registered representative lists for each committee)
- Standardizing HLSL - DirectX Developer Blog (TC57 Establishment Announcement)
- OWASP: CycloneDX Standardization and EU CRA Compliance
- Ecma International - Wikipedia
- History and evolution of JavaScript
- [Translation] ECMAScript 2021: Final Feature Set Confirmed
- github.com/tc39/notes/…