Based on Dylan Israel's awesome Scrimba Frontend Interview Tips. I wrote a few code examples to understand these concepts better. Shoutout to the awesome Josh Comeau for his incredible operator lookup tool.
Based on Dylan Israel's awesome Scrimba Frontend Interview Tips. I wrote a few code examples to understand these concepts better. Shoutout to the awesome Josh Comeau for his incredible operator lookup tool.
import Table, { H1 } from "../components/table-of-contents-q"
Undefined means that the variable has been declared but has not had a value assigned to it. Undeclared is that it hasn't been declared.
if (false) {
let firstNameLet = "mohammed
var firstNameVar = "mohammed"
}
console.log(firstNameVar) // undefined
console.log(firstNameLet) // ReferenceError: firstNameLet is not defined
A: A set of unique values that evaluate to false.
const falsy = [
null, 0, undefined, false, NaN, "", -0, 0n
]
console.log(falsy.some(el => el)) // false
A: const and let use a block scope (anything between curly brackets) while var uses lexical scope.
const stores values that will not be reassigned. They can be mutated.
const user = { name: "mohammed}
user.name = "Alex"
console.log(user) // { name: 'Alex' }
Var uses hoisting, this means that the variable is declared before any of the code runs. In JS, variable and function declarations are put into memory during the compile phase.
This is why you can call a function before it's been declared
catName("Tamal")
function catName(name) {
console.log("My cat's name is " + name)
// My cat's name is Tamal
}
JavaScript only hoists declarations, not initializations.
console.log(num) // undefined
var num // Declaration
num = 6 // Initialization
This operator checks to see if two values are equivalent. Returns a boolean value. == ignores the type and focuses on the value. === does a strict check and checks both type and value.
const value1 = 5
const value2 = "5"
console.log(value1 == value2) // true
console.log(value1 === value2) // false
Map applies the callback to each value. Returns an array with the results.
const nums = [1, 2, 3]
const numsAddOne = nums.map(value => value + 1)
console.log(numsAddOne) // [2,3,4]
Filter applies the callback but only passes the elements that return true.
const nums = [1, 2, 3, 4]
const evenNums = nums.filter(value => value % 2 === 0)
console.log(evenNums) // [2, 4]
Reduce takes a callback with two arguments, the first one is the accumulator, the second is the current value. It takes a second argument which is the initial accumulator value.
For each of the elements in the array, the callback is applied and the result becomes the new accumulator.
const nums = [1, 2, 3]
const sum = nums.reduce((total, current) => {
return total + current
}, 0)
console.log(sum) // 6
While undefined means that something doesn't exist, null means something has no value. It's preferable to have null values that might change later rather than undefined. Friendlier code
Primitives: String, boolean, numbers, BigInt, Symbol. Non-primitives: [ ], { }, Map (objects with unique keys that are beyond strings), Set (arrays with unique values)
The spread operator can be used to unwrap an array or an object.
It can also be used to clone or merge arrays and objects.
const users = ["dinosaur", "pumpkin", "cat"]
const allUsers = ["Mr. Dog", ...users]
console.log(users)
// [ 'dinosaur', 'pumpkin', 'cat' ]
console.log(allUsers)
// [ 'Mr. Dog', 'dinosaur', 'pumpkin', 'cat' ]
This operator is used in function definitions to collect additional function arguments. Useful when you don't know how many parameters a function needs. It collects them into an array.
function easy(a, b, c) {
console.log("Easy as", a, b, c)
}
const letters = ["A", "B", "C"]
easy(...letters) // Easy as A B C
concatenate an array
const a = ["Mr. Dog", "Mrs. Dog"]
const b = ["Pup", "Little Dog"]
const family = [...a, ...b]
console.log(family)
//[ 'Mr. Dog', 'Mrs. Dog', 'Pup', 'Little Dog' ]
Does the opposite of spread. It collects a function's arguments into an array. It's useful when you don't know how many parameters a function needs.
function order(...elements) {
elements.forEach(el => console.log(el))
}
order("fries", "milkshake", "cheesburger")
//fries
//milkshake
//cheesburger
Destrucuring provides context. It's intended to make code easier to read.
Destructuring
Destructuring objects
const scores = {
water: 5,
wine: 11,
milk: 4,
beer: 7,
}
const { wine: fav, ...others } = scores
console.log("How would you rate wine?", fav)
//How would you rate wine? 11
Now let's do arrays
const user = "Paul Hollywood π°"
const [first, last, emoji] = user.split(" ")
console.log(`${first} loves ${emoji}`)
//Paul loves π°
|| returns the first falsy value. If there are none, it returns the last element.
Think about it as detectives inside an interrogation room |π΅οΈββοΈπ΅οΈββοΈ|
They're trying to find true statements π
&& returns the first truthy value. If there are none, it returns the last element.
Think about this one as the superhero π¦ΈββοΈ with the && printed on their costume. They're looking for the false statements and bringing them to justice π
See them in action:
const isFrench = true
const greeting = str => console.log(str)
// If statement
if (isFrench) {
greeting("bonjour! if π₯")
} else {
greeting("hi! if π")
}
// bonjour! if π₯
// Ternary operator
isFrench ? greeting("bonjour! ? π₯") : greeting("hi! ? π")
// bonjour! ? π₯
// Or operator
!isFrench || greeting("bonjour! || π₯")
isFrench || greeting("hi! || π")
// bonjour! || π₯
// And operator
isFrench && greeting("bonjour! && π₯")
!isFrench && greeting("hi! && π")
// bonjour! && π₯
This cheatsheet is based on Dylan Israel's awesome Scrimba Frontend Interview Tips.
Checkout Josh Comeau's Operator Lookup for even more operator fun!
Practice Coding Out Loud: During interviews, verbalize your thought process. Explain why you're choosing specific methods or approaches.
Know the Time Complexity: Be prepared to discuss Big O notation for array methods:
map(), filter(), forEach(): O(n)find(), includes(): O(n) worst casesort(): O(n log n)Understand Memory Management: Know when methods create new arrays vs. mutate existing ones. This shows deep understanding of JavaScript's behavior.
Real-World Context: Connect concepts to practical applications. For example, explain how you'd use reduce() for data aggregation in a dashboard.
Destructuring Assignment:
// Array destructuring
const [first, second, ...rest] = [1, 2, 3, 4, 5]
console.log(first) // 1
console.log(rest) // [3, 4, 5]
// Object destructuring with renaming
const { name: userName, age } = { name: 'Alice', age: 30 }
console.log(userName) // 'Alice'
Template Literals and Tagged Templates:
const name = 'Mohammed'
const age = 25
// Template literal
const greeting = `Hello, I'm ${name} and I'm ${age} years old`
// Tagged template
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
const value = values[i] ? `<mark>${values[i]}</mark>` : ''
return result + string + value
}, '')
}
const highlighted = highlight`Name: ${name}, Age: ${age}`
Async/Await Patterns:
// Sequential execution
async function sequential() {
const result1 = await fetch('/api/data1')
const result2 = await fetch('/api/data2')
return [result1, result2]
}
// Parallel execution
async function parallel() {
const [result1, result2] = await Promise.all([
fetch('/api/data1'),
fetch('/api/data2')
])
return [result1, result2]
}
1. Event Loop Confusion:
console.log('1')
setTimeout(() => console.log('2'), 0)
Promise.resolve().then(() => console.log('3'))
console.log('4')
// Output: 1, 4, 3, 2
2. this Binding Issues:
const obj = {
name: 'Mohammed',
greet: function() { return `Hello, ${this.name}` },
greetArrow: () => `Hello, ${this.name}` // `this` is not bound!
}
console.log(obj.greet()) // "Hello, Mohammed"
console.log(obj.greetArrow()) // "Hello, undefined"
3. Closures and Memory Leaks:
// Potential memory leak
function createHandlers() {
const bigData = new Array(1000000).fill('data')
return {
handler: () => console.log('Handler called'),
// bigData is captured in closure even if not used
}
}
Efficient Array Operations:
// β Inefficient: Multiple iterations
const processed = data
.filter(item => item.active)
.map(item => item.value)
.filter(value => value > 0)
// β
Efficient: Single iteration with reduce
const processed = data.reduce((acc, item) => {
if (item.active && item.value > 0) {
acc.push(item.value)
}
return acc
}, [])
Debouncing for Search:
function debounce(func, delay) {
let timeoutId
return function (...args) {
clearTimeout(timeoutId)
timeoutId = setTimeout(() => func.apply(this, args), delay)
}
}
const debouncedSearch = debounce((query) => {
// API call
}, 300)
Mastering JavaScript is an ongoing journey. Here are your next steps:
Intermediate Concepts: Dive deeper into prototypes, design patterns, and advanced async programming.
Modern Frameworks: Apply these fundamentals in React, Vue, or Angular projects.
Node.js: Extend your JavaScript knowledge to backend development.
TypeScript: Add static typing to catch errors early and improve code quality.
Testing: Learn Jest, Cypress, or other testing frameworks to write robust applications.
Remember: The best way to master JavaScript is by building projects. Start with simple applications and gradually increase complexity as you become more comfortable with these concepts.
Need personalized JavaScript mentoring? I've helped 100+ developers master JavaScript fundamentals and land frontend roles at top tech companies. Whether you're preparing for interviews or looking to level up your skills, I can help you create a focused learning plan.
Good luck with your JavaScript journey! π
- π§‘ mohammed