Back to Hub
Frontend Fundamentals

ECMAScript History & Evolution

#ECMAScript#ES6#ES2015#ES2016#ES2017#ES2020#ES2022#JavaScript-history#TC39

ECMAScript History & Evolution

JavaScript was created in 10 days in 1995 by Brendan Eich at Netscape. From that chaotic origin, it has evolved into the world's most widely deployed programming language. This is the full story of every significant version.


The Standards Body — TC39

TC39 (Technical Committee 39) is the ECMA International committee responsible for the ECMAScript specification. It operates on a stage-based proposal process:

StageNameMeaning
0StrawpersonInitial idea, anyone can submit
1ProposalChampions assigned, problem/solution outlined
2DraftFormal spec text written
3CandidateSpec complete, implementations invited
4FinishedReady to be included in the next annual release

Since ES2015, TC39 releases a new edition every year (typically June), even if it's a small release.


ES1 — ES3 (1997–1999): The Beginning

ES1 (1997)

The first standardised version. Based on JavaScript 1.1 from Netscape.

  • Basic syntax, types, expressions, statements
  • typeof, instanceof operators

ES2 (1998)

Minor editorial changes to align with ISO/IEC 16262. No new features.

ES3 (1999) — First major version

A huge improvement used as the baseline for years:

  • Regular expressions
  • try/catch/finally exception handling
  • switch, do-while
  • String methods: match(), replace(), search(), split()
  • Proper undefined

ES3 became the compatibility target for IE6-8, which is why so many "legacy JS" patterns exist.


ES4 — Abandoned (2008)

ES4 was an ambitious proposal (classes, optional typing, packages, namespaces) driven by Macromedia/Adobe. Microsoft and others opposed it. The proposal was abandoned after years of committee deadlock.

The fallout led to a split:

  • "ES3.1" — conservative improvements (became ES5)
  • Harmony — a reset of bigger ideas (eventually became ES6)

ES5 (2009) — The Reliability Update

Used for nearly 6 years as the primary target. Key additions:

Strict Mode

javascript
'use strict'; // Enables strict mode for the file or function // Prevents: undeclared variables, duplicate params, `this` as global, etc.

Array Methods

javascript
Array.prototype.forEach() Array.prototype.map() Array.prototype.filter() Array.prototype.reduce() / reduceRight() Array.prototype.some() / every() Array.prototype.indexOf() / lastIndexOf() Array.isArray()

Object Methods

javascript
Object.create() // prototypal inheritance Object.defineProperty() // property descriptors Object.keys() // enumerable own keys Object.freeze() // immutable objects Object.seal() Object.getPrototypeOf()

JSON

javascript
JSON.parse() JSON.stringify()

Function.prototype.bind()

javascript
const bound = fn.bind(context, ...partialArgs);

ES5.1 (2011)

Editorial fixes. ES5.1 is what browsers actually shipped.


ES6 / ES2015 — The Revolution

The biggest update in JavaScript's history. Took ~6 years to standardise (2009–2015). Transformed the language.

let and const

javascript
let mutable = 1; const IMMUTABLE = 2; // Block-scoped, TDZ, no re-declaration

Arrow Functions

javascript
const add = (a, b) => a + b; // Lexical `this` — no own this binding

Template Literals

javascript
const msg = `Hello, ${name}! You have ${count} messages.`; const multiline = ` line one line two `;

Destructuring

javascript
const { name, age } = user; const [first, ...rest] = array;

Default Parameters

javascript
function greet(name = 'World') { return `Hello, ${name}!`; }

Rest & Spread

javascript
function sum(...nums) { return nums.reduce((a, b) => a + b, 0); } const merged = { ...obj1, ...obj2 }; const copy = [...arr];

Classes

javascript
class Animal { constructor(name) { this.name = name; } speak() { return `${this.name} speaks.`; } } class Dog extends Animal { speak() { return `${this.name} barks.`; } }

Modules (ESM)

javascript
export const PI = 3.14; export default function main() {} import PI, { helper } from './module.js';

Promises

javascript
fetch('/api').then(r => r.json()).catch(console.error);

Generators

javascript
function* gen() { yield 1; yield 2; yield 3; }

Symbols

javascript
const id = Symbol('id');

Map, Set, WeakMap, WeakSet

javascript
const map = new Map(); map.set(key, value); const set = new Set([1, 2, 3]);

for...of Loop

javascript
for (const item of iterable) {}

Proxy & Reflect

Promise

String enhancements

javascript
'hello'.startsWith('he') // true 'hello'.endsWith('lo') // true 'hello'.includes('ell') // true 'ha'.repeat(3) // "hahaha" '5'.padStart(3, '0') // "005"

Number enhancements

javascript
Number.isInteger(42) // true Number.isFinite(Infinity) // false Number.isNaN(NaN) // true Number.parseInt() // same as global Number.EPSILON Number.MAX_SAFE_INTEGER

ES2016 (ES7) — Small but Meaningful

Array.prototype.includes()

javascript
[1, 2, NaN].includes(NaN); // true — unlike indexOf

Exponentiation Operator

javascript
2 ** 10; // 1024 — instead of Math.pow(2, 10) 2 **= 3; // compound assignment

ES2017 (ES8) — Async/Await

async / await

javascript
async function fetchUser(id) { const res = await fetch(`/api/users/${id}`); return res.json(); }

Object.entries() / Object.values()

javascript
Object.entries({ a: 1, b: 2 }); // [['a', 1], ['b', 2]] Object.values({ a: 1, b: 2 }); // [1, 2] Object.fromEntries([['a', 1]]); // { a: 1 } ← added ES2019

String Padding

javascript
'5'.padStart(5, '0'); // "00005" 'hi'.padEnd(5, '.'); // "hi..."

Object.getOwnPropertyDescriptors()

Shared Memory & Atomics (SharedArrayBuffer)

Trailing Commas in Function Parameters


ES2018 (ES9) — Async Iteration

Rest/Spread for Objects

javascript
const { a, ...rest } = { a: 1, b: 2, c: 3 }; // rest = { b: 2, c: 3 }

Async Iteration

javascript
for await (const chunk of asyncIterable) {}

Promise.finally()

javascript
fetch('/api').then(use).catch(handle).finally(cleanup);

Named Capture Groups in Regex

javascript
const match = '2025-06-07'.match(/(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/); match.groups.year; // "2025" match.groups.month; // "06"

s (dotAll) Flag in Regex

javascript
/hello.world/s.test('hello\nworld'); // true — . matches newline

ES2019 (ES10)

Array.prototype.flat() / flatMap()

javascript
[1, [2, [3]]].flat(); // [1, 2, [3]] [1, [2, [3]]].flat(Infinity); // [1, 2, 3] [1, 2, 3].flatMap(x => [x, x * 2]); // [1, 2, 2, 4, 3, 6]

Object.fromEntries()

javascript
const obj = Object.fromEntries(map); // Map → Object const doubled = Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, v * 2]));

String.prototype.trimStart() / trimEnd()

javascript
' hi '.trimStart(); // "hi " ' hi '.trimEnd(); // " hi"

Optional Catch Binding

javascript
try { JSON.parse(x); } catch { /* no need to name the error */ }

Array.prototype.sort() guaranteed stable

Previously implementation-defined; now spec requires stable sort.


ES2020 (ES11)

Optional Chaining ?.

javascript
user?.address?.city; // undefined if any link is null/undefined arr?.[0]; // safe array index fn?.(); // safe function call

Nullish Coalescing ??

javascript
const name = user.name ?? 'Guest'; // only falls back on null/undefined const port = config.port ?? 3000;

??=, ||=, &&= (Logical Assignment — added ES2021)

BigInt

javascript
const huge = 9007199254740993n; typeof huge; // "bigint"

Promise.allSettled()

javascript
const results = await Promise.allSettled([p1, p2, p3]); // Always resolves; each result is { status: 'fulfilled'|'rejected', value|reason }

globalThis

javascript
// Universal reference to the global object (window/global/self) globalThis.setTimeout(() => {}, 0);

Dynamic import()

javascript
const module = await import('./module.js');

String.prototype.matchAll()

javascript
const matches = [...'aabbcc'.matchAll(/([a-z])\1/g)];

ES2021 (ES12)

String.prototype.replaceAll()

javascript
'a-b-c'.replaceAll('-', '_'); // "a_b_c"

Logical Assignment Operators

javascript
a ||= b; // a = a || b a &&= b; // a = a && b a ??= b; // a = a ?? b

Promise.any()

javascript
// Resolves with first fulfilled; rejects with AggregateError if all reject const first = await Promise.any([p1, p2, p3]);

Numeric Separators

javascript
const million = 1_000_000; const hex = 0xFF_EC_D9_12; const bytes = 0b1010_0001;

WeakRef & FinalizationRegistry


ES2022 (ES13)

Class Fields — Public, Private, Static

javascript
class Counter { count = 0; // public instance field #secret = 42; // private instance field static instances = 0; // static field static #id = 0; // private static field #increment() { this.count++; } // private method static create() { return new Counter(); } // static method }

Array.prototype.at()

javascript
[1, 2, 3].at(-1); // 3 — negative indexing 'hello'.at(-1); // 'o'

Object.hasOwn()

javascript
Object.hasOwn(obj, 'prop'); // safer than obj.hasOwnProperty('prop')

Top-Level await

javascript
// In ESM modules — no need for async wrapper const data = await fetch('/api').then(r => r.json()); export { data };

Error Cause

javascript
throw new Error('Failed to load user', { cause: originalError }); err.cause; // the original error

Array.prototype.findLast() / findLastIndex()


ES2023 (ES14)

Array Methods on Copies (Non-mutating)

javascript
arr.toSorted(); // sorted copy (vs arr.sort() which mutates) arr.toReversed(); // reversed copy arr.toSpliced(1, 2, 'x'); // spliced copy arr.with(2, 'x'); // copy with index 2 replaced

Array.prototype.findLast() / findLastIndex() (formalised)

Hashbang (#!) Support for Scripts

javascript
#!/usr/bin/env node console.log('Hello');

ES2024 (ES15)

Object.groupBy() / Map.groupBy()

javascript
const grouped = Object.groupBy(users, user => user.role); // { admin: [...], user: [...] }

Promise.withResolvers()

javascript
const { promise, resolve, reject } = Promise.withResolvers(); // Cleaner than: new Promise((resolve, reject) => { ... })

String.prototype.isWellFormed() / toWellFormed()

ArrayBuffer.prototype.transfer()

Atomics.waitAsync()


ES2025 (ES16) — In Progress

using — Explicit Resource Management

javascript
{ using db = new DatabaseConnection(); await using file = await openFile('data.csv'); } // both automatically disposed

Iterator Helpers

javascript
Iterator.from([1, 2, 3, 4, 5]) .filter(x => x % 2 === 0) .map(x => x * 10) .toArray(); // [20, 40]

RegExp.escape()

javascript
const escaped = RegExp.escape('1 + 1 = 2'); // '1 \\+ 1 = 2' new RegExp(escaped); // safely use user input in regex

Set Methods

javascript
a.union(b) a.intersection(b) a.difference(b) a.symmetricDifference(b) a.isSubsetOf(b) a.isSupersetOf(b)

ECMAScript Timeline Summary

VersionYearHeadline Feature
ES11997First standard
ES31999Regex, try/catch
ES52009Strict mode, Array.map, JSON
ES6/ES20152015let/const, classes, modules, Promises, arrow fn, destructuring
ES20162016includes(), ** operator
ES20172017async/await, Object.entries()
ES20182018Object rest/spread, Promise.finally
ES20192019flat(), fromEntries(), optional catch
ES20202020?., ??, BigInt, globalThis
ES20212021replaceAll(), logical assignment, Promise.any
ES20222022Private class fields, top-level await, at()
ES20232023Non-mutating array methods
ES20242024Object.groupBy(), Promise.withResolvers()
ES20252025using, Iterator helpers, Set methods