10 JavaScript Tricks That Will Save You Hours of Coding Headaches

JavaScript is full of hidden gems that can transform verbose code into elegant solutions. After years of working with JS across production applications, I've curated these battle-tested tricks that consistently save development time. Master these and you'll write cleaner, more efficient code immediately.
1. Destructuring with Aliases & Default Values
The Problem: Nested object properties require repetitive null checks.
The Solution:
// Before:
const street = user.address ? user.address.street : 'Unknown';
// After:
const { address: { street: userStreet = 'Unknown' } = {} } = user;
Why it rocks:
- Aliases (
street: userStreet
) prevent naming conflicts - Default values (
= 'Unknown'
) handle undefined cases - Nested destructuring avoids multiple checks
2. Optional Chaining + Nullish Coalescing Combo
The Problem: Endless a && a.b && a.b.c
chains for safe access.
The Solution:
// Safely access nested properties
const price = order?.items?.[0]?.price ?? 0;
// Also works with functions:
user.getAddress?.()?.postcode;
Pro Tip: Combine with ||
for falsy values, but use ??
when 0
or false
are valid:
const discount = cart?.discount ?? 15; // 0 won't be overridden
3. Dynamic Object Keys with Computed Properties
The Problem: Creating objects with dynamic keys requires temporary variables.
The Solution:
const key = 'mobile';
const phoneTypes = {
[key]: 'iPhone',
[`${key}Model`]: '15 Pro' // Computed key names
};
// { mobile: 'iPhone', mobileModel: '15 Pro' }
Use Case: API response normalization where keys depend on data.
4. Array Filtering Truthy Values
The Problem: Removing null
, undefined
, 0
, ''
from arrays manually.
The Solution:
const data = [0, 1, '', 'text', null, undefined];
const clean = data.filter(Boolean); // [1, 'text']
How it works: The Boolean
constructor acts as the predicate function.
5. Object Cloning with StructuredClone
The Problem: JSON.parse(JSON.stringify())
fails with Dates, Sets, Maps, and functions.
The Solution:
const deepCopy = structuredClone(original);
// Handles:
// - Date objects
// - Map/Set
// - Circular references
Browser Support: All modern browsers (Node.js 17+)
6. Short-circuit Function Execution
The Problem: Conditional function calls create extra code branches.
The Solution:
// Instead of:
if (isLoggedIn) { fetchData(); }
// Do:
isLoggedIn && fetchData();
// Set defaults:
const apiUrl = env.API_URL || 'https://default.api';
Warning: Avoid for critical logic where clarity is more important. Use traditional conditionals there.
7. Array Deduplication in One Line
The Problem: Removing duplicates with complex loops or external libraries.
The Solution:
const dupes = [1, 2, 2, 3, 4, 4];
const unique = [...new Set(dupes)]; // [1, 2, 3, 4]
// For objects based on a key:
const objSet = [...new Map(users.map(u => [u.id, u])).values()];
8. Function Parameter Destructuring
The Problem: Long, hard-to-read parameter lists with optional configs.
The Solution:
// Before:
function connect(host, port, timeout=10, retries=3) { /*...*/ }
// After:
function connect({ host, port, timeout = 10, retries = 3 }) {
console.log(`Connecting to ${host}:${port}`);
}
// Usage:
connect({ port: 8080, host: 'api.com' });
Benefits:
- Self-documenting parameters
- Order independence
- Clear default values for missing props
9. Console Debugging Superpowers
The Problem: Basic console.log
provides minimal context and clutters your view.
Advanced Tactics:
// 1. Table formatting for objects/arrays:
console.table(users.filter(u => u.active));
// 2. Style debug messages for visibility:
console.log('%cPayment Successful!', 'color: green; font-weight: bold; font-size: 16px;');
// 3. Performance measurement:
console.time('render');
renderComponent();
console.timeEnd('render'); // logs execution time
// 4. Stack traces for debugging call order:
console.trace('Function called from:');
10. URL Parameter Parsing
The Problem: Manually parsing query strings with regex or split()
.
The Solution:
const params = new URLSearchParams(window.location.search);
// Get single param
const id = params.get('id');
// Convert to object:
const queryObj = Object.fromEntries(params.entries());
// Update params and URL without reloading:
params.set('page', 2);
history.replaceState(null, '', `?${params.toString()}`);
Bonus: Modern Error Handling
The Problem: Nested try/catch
hell or unhandled promise rejections.
The Solution:
// 1. Promise error handling with grace:
const data = await fetchData().catch(error => {
logError(error);
return fallbackData; // Return a default value to prevent app crash
});
// 2. Error boundary pattern (React):
function ErrorBoundary({ children }) {
try {
return children;
} catch (error) {
return <FallbackUI error={error} />;
}
}
When NOT to Use These Tricks
While powerful, avoid these patterns when they harm readability:
- Short-circuiting for critical logic: An explicit
if
statement is always clearer for business rules. - Overly nested optional chaining: If data is missing deep in a structure, it might indicate a bigger problem that shouldn't be silenced.
- "Clever" one-liners: If team members aren't familiar with a pattern, a more verbose but understandable version is better.
"The art of writing code is knowing when not to be clever."
Level Up Your Workflow
Implement these immediately:
- Replace all deep-access null checks with
?.
and??
. - Refactor any function with more than 3 parameters to use object destructuring.
- Use
console.table()
for your next array debugging session. You'll never go back.