<!-- SPDX-License-Identifier: MIT -->
<!-- SPDX-FileCopyrightText: 2025-2026 Marcus Quinn -->

# ES2016 & ES2017 Features

## ES2016 (ES7)

### Array.prototype.includes()

Checks if array contains a value; handles `NaN` correctly (unlike `indexOf`).

```javascript
const arr = [1, 2, 3, NaN];
arr.includes(2);      // true
arr.includes(4);      // false
arr.includes(NaN);    // true  (indexOf returns -1 here)
arr.includes(2, 2);   // false (searches from index 2)
```

### Exponentiation Operator (`**`)

```javascript
2 ** 10;      // 1024  (replaces Math.pow(2, 10))
2 ** 3 ** 2;  // 512   (right-associative: 2 ** 9)

let x = 2;
x **= 3;      // 8
```

## ES2017 (ES8)

### Async Functions

Syntactic sugar over Promises for cleaner asynchronous code; use `try/catch` for error handling.

```javascript
async function fetchUser(id) {
  try {
    const res = await fetch(`/api/users/${id}`);
    const user = await res.json();
    const postsRes = await fetch(`/api/posts?userId=${user.id}`);
    return await postsRes.json();
  } catch (err) {
    console.error(err);
  }
}

// Arrow and method syntax
const getUser = async (id) => { const res = await fetch(`/api/users/${id}`); return res.json(); };
const obj = { async getData() { return await fetchSomething(); } };
```

### Object.values() and Object.entries()

```javascript
const user = { name: 'Alice', age: 30, city: 'NYC' };
Object.values(user);   // ['Alice', 30, 'NYC']
Object.entries(user);  // [['name', 'Alice'], ['age', 30], ['city', 'NYC']]

for (const [key, value] of Object.entries(user)) {
  console.log(`${key}: ${value}`);
}

const doubled = Object.fromEntries(
  Object.entries(prices).map(([k, v]) => [k, v * 2])
);
```

### String Padding

```javascript
'5'.padStart(3, '0');   // '005'
'42'.padStart(5, '*');  // '***42'
'hello'.padStart(10);   // '     hello'

'5'.padEnd(3, '0');     // '500'
'hi'.padEnd(5, '.');    // 'hi...'

const formatId = id => String(id).padStart(6, '0');
formatId(42);  // '000042'
```

### Object.getOwnPropertyDescriptors()

Returns all property descriptors; preserves getters/setters lost by `Object.assign`.

```javascript
const source = {
  name: 'Alice',
  get fullName() { return this.name; }
};

// Object.assign loses the getter — fullName becomes a static value
const bad = Object.assign({}, source);

// Preserve descriptors
const good = Object.defineProperties({}, Object.getOwnPropertyDescriptors(source));
```

### Trailing Commas in Function Parameters

Allows trailing commas in parameter lists and arguments for cleaner version-control diffs.

```javascript
function greet(
  name,
  greeting,  // trailing comma allowed
) { return `${greeting}, ${name}!`; }

greet('Alice', 'Hello',);  // trailing comma in call too
```

### SharedArrayBuffer and Atomics

Low-level shared memory and thread-safe operations for Web Workers.

```javascript
const buffer = new SharedArrayBuffer(16);
const view = new Int32Array(buffer);

Atomics.store(view, 0, 42);
Atomics.load(view, 0);                    // 42
Atomics.add(view, 0, 1);                  // returns 42; view[0] -> 43
Atomics.compareExchange(view, 0, 43, 100); // if 43, set to 100
```
