JavaScript Gems — 15 Underused Features That Make Your Code Cleaner in 2026
Advertisement
Introduction
Why This Matters
JavaScript adds new syntax and APIs every year, but most developers continue reaching for the patterns they learned first. The result is code that is longer, more fragile, and harder to read than it needs to be. This guide highlights 15 features — most of them stable since ES2020 or later — that professional teams now use as standard practice. Each one replaces a verbose or error-prone pattern with something shorter and more expressive.
Gem 1 — Optional Chaining (?.)
Eliminates defensive && chains when navigating nested objects:
// ❌ Old pattern — verbose and fragile
const city = user && user.address && user.address.city
// ✅ Optional chaining
const city = user?.address?.city
// Works on method calls and array indexing
const firstTag = article?.tags?.[0]
const formatted = response?.data?.format?.()
// Combine with nullish coalescing for a safe default
const displayCity = user?.address?.city ?? 'Unknown'Gem 2 — Nullish Coalescing (??) vs Logical OR (||)
?? only falls back on null or undefined, while || falls back on any falsy value. This distinction matters:
const timeout = config.timeout ?? 3000
// Safe: if config.timeout = 0, we get 0 (not 3000)
const timeout2 = config.timeout || 3000
// Bug: if config.timeout = 0, we get 3000 (wrong!)
// Nullish assignment — only assigns if the left side is null/undefined
user.preferences ??= {}
user.preferences.theme ??= 'dark'Gem 3 — Logical Assignment Operators
ES2021 added &&=, ||=, and ??= — assignment that is conditional on the current value:
// ??= — assign only if null/undefined
let config = {}
config.retries ??= 3 // sets to 3 (was undefined)
config.retries ??= 99 // no-op (already 3)
// ||= — assign only if falsy
let label = ''
label ||= 'Default Label' // 'Default Label' (was empty string)
// &&= — assign only if truthy
let user = { name: 'Alice', admin: true }
user.admin &&= false // sets to false (was truthy)Gem 4 — Array.at() — Clean Negative Indexing
Array.at() accepts negative indices, making last-element access clean without .length - 1:
const items = ['first', 'second', 'third', 'last']
// ❌ Old way
console.log(items[items.length - 1]) // 'last'
console.log(items[items.length - 2]) // 'third'
// ✅ Array.at()
console.log(items.at(-1)) // 'last'
console.log(items.at(-2)) // 'third'
console.log(items.at(0)) // 'first' (also works with positive)
// Works on strings too
const str = 'JavaScript'
console.log(str.at(-1)) // 't'Gem 5 — structuredClone() — Deep Cloning Done Right
Forget JSON.parse(JSON.stringify(...)) — structuredClone() handles dates, maps, sets, circular references, and typed arrays:
const original = {
name: 'Alice',
birthdate: new Date('1995-01-01'), // Date object
scores: new Set([95, 87, 100]), // Set
metadata: new Map([['role', 'admin']]), // Map
}
// ❌ JSON approach fails silently
const broken = JSON.parse(JSON.stringify(original))
console.log(broken.birthdate instanceof Date) // false — became a string
console.log(broken.scores instanceof Set) // false — became an object
// ✅ structuredClone handles all standard types correctly
const clone = structuredClone(original)
console.log(clone.birthdate instanceof Date) // true
console.log(clone.scores instanceof Set) // true
clone.scores.add(70)
console.log(original.scores.has(70)) // false — independent deep copyGem 6 — Object.hasOwn() Over hasOwnProperty
Object.hasOwn() is the safe, modern replacement for .hasOwnProperty():
const obj = { name: 'Alice', age: 30 }
// ❌ Old pattern — can break if obj overrides hasOwnProperty
if (obj.hasOwnProperty('name')) { ... }
// ✅ Modern — always safe
if (Object.hasOwn(obj, 'name')) { ... }
// Practical use: filtering inherited properties
const entries = Object.keys(obj).filter(key => Object.hasOwn(obj, key))Gem 7 — Tagged Template Literals
Tagged templates let you process template literal expressions through a function — perfect for sanitisation, i18n, and SQL:
// SQL sanitisation — prevents injection
function sql(strings, ...values) {
return {
text: strings.reduce((acc, s, i) => acc + s + (i < values.length ? `$${i + 1}` : ''), ''),
values: values,
}
}
const userId = 42
const query = sql`SELECT * FROM users WHERE id = ${userId}`
// { text: 'SELECT * FROM users WHERE id = $1', values: [42] }
// i18n with dynamic locale
function i18n(strings, ...values) {
return strings.reduce((acc, s, i) =>
acc + s + (values[i] !== undefined ? String(values[i]) : ''), '')
}
const greeting = i18n`Hello, ${'Alice'}! You have ${5} messages.`
// 'Hello, Alice! You have 5 messages.'Gem 8 — Destructuring with Defaults and Renaming
Destructuring is more powerful than most developers use it:
// Rename and provide defaults in one expression
const { name: userName = 'Guest', role: userRole = 'viewer' } = user ?? {}
// Array destructuring with skip
const [first, , third] = [1, 2, 3] // second is skipped
// Nested destructuring
const { address: { city, country = 'US' } } = user
// Function parameter destructuring with defaults
function createUser({ name, role = 'user', active = true } = {}) {
return { name, role, active, createdAt: new Date() }
}
createUser({ name: 'Alice' })
// { name: 'Alice', role: 'user', active: true, createdAt: ... }Gem 9 — Spread and Rest in Creative Ways
// Merge objects with override priority (rightmost wins)
const defaults = { timeout: 3000, retries: 3, debug: false }
const userConfig = { timeout: 5000, apiKey: 'abc123' }
const config = { ...defaults, ...userConfig }
// { timeout: 5000, retries: 3, debug: false, apiKey: 'abc123' }
// Clone and remove a property at the same time
const { password, ...safeUser } = user // password excluded
// Collect remaining function arguments
function log(level, ...messages) {
console[level](messages.join(' '))
}
log('warn', 'Disk', 'is', '90%', 'full')
// Convert any iterable to an array
const unique = [...new Set([1, 2, 2, 3, 3, 4])] // [1, 2, 3, 4]Gem 10 — Promise Combinators Beyond Promise.all()
const requests = [
fetch('/api/users'),
fetch('/api/products'),
fetch('/api/settings'),
]
// Promise.all — fails fast if ANY rejects
const [users, products, settings] = await Promise.all(requests)
// Promise.allSettled — waits for ALL, reports success or failure each
const results = await Promise.allSettled(requests)
results.forEach(r => {
if (r.status === 'fulfilled') console.log('OK:', r.value)
else console.log('ERR:', r.reason)
})
// Promise.race — resolves/rejects with the FIRST to settle
const winner = await Promise.race([fetchPrimary(), fetchFallback()])
// Promise.any — resolves with the FIRST to FULFILL (ignores rejections)
const fastest = await Promise.any([mirror1(), mirror2(), mirror3()])Gem 11 — WeakRef and FinalizationRegistry
For advanced memory management — hold a reference that does not prevent garbage collection:
class Cache {
#store = new Map()
set(key, value) {
this.#store.set(key, new WeakRef(value))
}
get(key) {
const ref = this.#store.get(key)
if (!ref) return undefined
const value = ref.deref() // returns undefined if GC'd
if (!value) {
this.#store.delete(key) // clean up
return undefined
}
return value
}
}Gem 12 — Object.fromEntries() — Transforming Maps and Arrays
// Transform object values
const prices = { apple: 1.5, banana: 0.75, cherry: 3.0 }
const discounted = Object.fromEntries(
Object.entries(prices).map(([k, v]) => [k, v * 0.9])
)
// { apple: 1.35, banana: 0.675, cherry: 2.7 }
// Convert Map to plain object
const map = new Map([['a', 1], ['b', 2], ['c', 3]])
const obj = Object.fromEntries(map)
// { a: 1, b: 2, c: 3 }
// Parse URL query string
const params = new URLSearchParams('page=2&limit=20&sort=asc')
const queryObj = Object.fromEntries(params)
// { page: '2', limit: '20', sort: 'asc' }Gem 13 — Computed Property Names and Short-hand Methods
const fieldName = 'email'
const prefix = 'get'
const form = {
[fieldName]: '', // computed key: 'email'
[`${prefix}Email`]() { // computed method: 'getEmail'
return this[fieldName]
},
validate() { return this.email.includes('@') }, // shorthand method
}Gem 14 — String Methods: trimStart, trimEnd, replaceAll, at
const raw = ' hello world '
console.log(raw.trimStart()) // 'hello world '
console.log(raw.trimEnd()) // ' hello world'
console.log(raw.trim()) // 'hello world'
const text = 'foo-bar-baz-bar'
console.log(text.replaceAll('bar', 'qux')) // 'foo-qux-baz-qux'
// Before replaceAll: text.replace(/bar/g, 'qux')
console.log('JavaScript'.at(-6)) // 'S'
console.log('hello'.padStart(10, '*')) // '*****hello'
console.log('hi'.padEnd(5, '!')) // 'hi!!!'Gem 15 — AbortController — Cancellable Fetch Requests
function fetchWithTimeout(url, timeoutMs = 5000) {
const controller = new AbortController()
const timer = setTimeout(() => controller.abort(), timeoutMs)
return fetch(url, { signal: controller.signal })
.then(res => {
clearTimeout(timer)
return res.json()
})
.catch(err => {
if (err.name === 'AbortError') throw new Error('Request timed out')
throw err
})
}
// Cancel on user action (e.g. search input)
let searchController = null
async function search(query) {
searchController?.abort() // cancel previous
searchController = new AbortController()
const results = await fetch(`/api/search?q=${query}`, {
signal: searchController.signal,
}).then(r => r.json())
return results
}Common Mistakes
- Using
||instead of??for default values —||incorrectly replaces0,false, and''with the fallback. - Forgetting that
?.short-circuits the entire expression —obj?.method().resultwill not throw ifobjis null, butresultwill beundefined. - Using
JSON.parse(JSON.stringify(x))for deep cloning — it silently corruptsDate,Map,Set, and circular structures. - Calling
.hasOwnProperty()directly on objects that may have overridden it — useObject.hasOwn()instead. - Not aborting fetch requests on component unmount in React — leads to state-update-on-unmounted-component warnings.
Best Practices
- Adopt
?.and??everywhere you access optional data; they make intent explicit and remove noise. - Use
structuredClone()for all deep copy operations — it is faster and more correct than JSON round-trip. - Use
Promise.allSettled()when you need all results regardless of individual failures. - Leverage
Object.fromEntries()paired withObject.entries().map()for clean object transformations. - Use
AbortControllerfor any fetch request that may become stale (search inputs, pagination, component unmounts).
Key Takeaways
- Optional chaining (
?.) and nullish coalescing (??) replace defensive&&chains and|| defaultpatterns with shorter, semantically correct code. ??only falls back onnullorundefined;||falls back on any falsy value — they are not interchangeable when0or''are valid values.structuredClone()correctly deep-clones Date, Map, Set, typed arrays, and circular references — use it instead of the JSON round-trip hack.Array.at(-1)replacesarr[arr.length - 1]for clean negative indexing.Object.hasOwn(obj, key)is the safe replacement forobj.hasOwnProperty(key).Promise.allSettled()waits for all promises and reports each outcome individually, unlikePromise.all()which fails fast.AbortControllerallows fetch requests to be cancelled on timeout or on user-triggered navigation.- Tagged template literals let you build safe SQL queries, i18n strings, and HTML sanitisation without external libraries.
Advertisement