React: The library for web and native user interfaces
Destructing Objects and Arrays
const numbers = [10, 20, 30];
// Without destructuring
const first = numbers[0];
const second = numbers[1];
// With destructuring
const [a, b] = numbers;
console.log(a); // 10
console.log(b); // 20
const person = {
name: "Alice",
age: 25,
city: "Taipei"
};
// Without destructuring
const name1 = person.name;
const age1 = person.age;
// With destructuring
const { name, age } = person;
console.log(name); // Alice
console.log(age); // 25
Rest/Spread Operator
Spread Operator (...
)
Expands (spreads) an array or object into individual elements.
const nums = [1, 2, 3];
const moreNums = [0, ...nums, 4, 5];
console.log(moreNums); // [0, 1, 2, 3, 4, 5]
function sum(a, b, c) {
return a + b + c;
}
const numbers = [10, 20, 30];
console.log(sum(...numbers)); // 60
Rest Operator (...
)
Collects multiple elements into a single array (or object).
const [first, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(rest); // [2, 3, 4, 5]
const { name, ...otherInfo } = { name: "Alice", age: 25, city: "Taipei" };
console.log(name); // Alice
console.log(otherInfo); // { age: 25, city: "Taipei" }
function sumAll(...numbers) {
return numbers.reduce((acc, num) => acc + num, 0);
}
console.log(sumAll(1, 2, 3, 4)); // 10
Template Literals
const name = "Alice";
const age = 25;
const intro = `My name is ${name} and I am ${age} years old.`;
console.log(intro);
// My name is Alice and I am 25 years old.
const a = 10, b = 20;
console.log(`The sum is ${a + b}`);
// The sum is 30
Multi-line Strings
No need for \n
or string concatenation:
const poem = `
Roses are red,
Violets are blue,
JavaScript is fun,
And so are you!
`;
console.log(poem);
Ternaries Instead of If/else Statements
The ternary operator is a shorthand for an if/else
statement.
Syntax:
condition ? expressionIfTrue : expressionIfFalse
let age = 20;
let status;
if (age >= 18) {
status = "Adult";
} else {
status = "Minor";
}
console.log(status); // Adult
let age = 20;
let status = age >= 18 ? "Adult" : "Minor";
console.log(status); // Adult
const score = 85;
const grade =
score >= 90 ? "A" :
score >= 80 ? "B" :
score >= 70 ? "C" :
"F";
console.log(grade); // B
Arrow Function
An arrow function is a shorter way to write a function, introduced in ES6 (2015).
It uses the =>
syntax.
function add(a, b) {
return a + b;
}
const add = (a, b) => a + b;
console.log(add(2, 3)); // 5
const greet = (name) => {
const message = `Hello, ${name}`;
return message;
};
const person = {
name: "Alice",
greet: function() {
setTimeout(() => {
console.log(`Hi, I'm ${this.name}`);
}, 1000);
}
};
person.greet(); // Hi, I'm Alice
Short-Circuiting And Logical Operators
The main ones are:
||
(OR)&&
(AND)!
(NOT)??
(Nullish coalescing, added in ES2020)
console.log(true || false); // true
console.log(false || "Hello"); // "Hello"
console.log(0 || "Default"); // "Default"
let user = "";
let username = user || "Guest";
console.log(username); // Guest
console.log(true && "Hi"); // "Hi"
console.log(false && "Hi"); // false
console.log(0 && "Hello"); // 0
let isLoggedIn = true;
isLoggedIn && console.log("Welcome back!");
// Logs only if isLoggedIn is true
console.log(!true); // false
console.log(!0); // true
console.log(!!"Hello"); // true
console.log(!!0); // false
console.log(null ?? "Default"); // "Default"
console.log(undefined ?? "Guest"); // "Guest"
console.log(0 ?? 100); // 0 (since 0 is not null/undefined)
let count = 0;
console.log(count || 10); // 10 (bad — treats 0 as falsy)
console.log(count ?? 10); // 0 (good — 0 is valid)
Summary Table
Operator | Returns | Example |
---|---|---|
` | ` (OR) | |
&& (AND) | First falsy value (or last if all truthy) | 1 && "Hi" → "Hi" |
! (NOT) | Inverts truthiness | !0 → true |
?? (Nullish) | Right side only if left is null /undefined | 0 ?? 10 → 0 |
Optional Chaning
Optional chaining (?.
) lets you safely access nested properties of an object without throwing an error if something is null
or undefined
.
const user = {};
console.log(user.profile.email);
// ❌ TypeError: Cannot read property 'email' of undefined
console.log(user?.profile?.email);
// ✅ undefined (no error)
object?.property
object?.[expression]
object?.method?.(args)
const person = {
name: "Alice",
address: { city: "Taipei" }
};
console.log(person?.address?.city); // Taipei
console.log(person?.contact?.phone); // undefined (safe!)
const user = {
sayHi() { return "Hi!"; }
};
console.log(user.sayHi?.()); // Hi!
console.log(user.sayBye?.()); // undefined (no error)
const arr = [1, 2, 3];
console.log(arr?.[1]); // 2
console.log(arr?.[5]); // undefined
const city = person && person.address && person.address.city;
const city = person?.address?.city;
Combining with Nullish Coalescing (??
)
Optional chaining returns undefined
if the property doesn’t exist, so you can give defaults with ??
const user = {};
const email = user?.profile?.email ?? "No email provided";
console.log(email); // "No email provided"
Array map
map()
is an array method in JavaScript.
It creates a new array by applying a function to each element of the original array.
It does not change the original array.
array.map(callback(currentValue, index, array), thisArg);
callback
→ function applied to each element.
currentValue
→ current element being processed.
index
(optional) → index of current element.
array
(optional) → the whole array.
thisArg
(optional) → value of this
inside callback.
const numbers = [1, 2, 3, 4];
const doubled = numbers.map(num => num * 2);
console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4] (unchanged)
const names = ["Alice", "Bob", "Carol"];
const withIndex = names.map((name, index) => `${index + 1}. ${name}`);
console.log(withIndex);
// ["1. Alice", "2. Bob", "3. Carol"]
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
];
const namesOnly = users.map(user => user.name);
console.log(namesOnly); // ["Alice", "Bob"]
const prices = ["$10", "$20", "$30"];
const numbers = prices.map(price => Number(price.slice(1)));
console.log(numbers); // [10, 20, 30]
map()
= transform each element of an array → new array.
Cleaner than writing a for
loop.
Very powerful when working with arrays of objects, API responses, or rendering UI lists.
Array filter
filter()
is an array method in JavaScript.
It creates a new array containing only the elements that pass a condition (the callback returns true
).
It does not change the original array.
array.filter(callback(currentValue, index, array), thisArg);
callback
→ function that decides whether to keep each element (true
) or discard it (false
).
currentValue
→ the current element being processed.
index
(optional) → index of current element.
array
(optional) → the original array.
thisArg
(optional) → value of this
inside callback.
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter(num => num % 2 === 0);
console.log(evens); // [2, 4, 6]
console.log(numbers); // [1, 2, 3, 4, 5, 6] (unchanged)
const names = ["Alice", "Bob", "Anna", "Carol"];
const aNames = names.filter(name => name.startsWith("A"));
console.log(aNames); // ["Alice", "Anna"]
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 17 },
{ name: "Carol", age: 30 }
];
const adults = users.filter(user => user.age >= 18);
console.log(adults);
// [ { name: "Alice", age: 25 }, { name: "Carol", age: 30 } ]
const mixed = [0, "Hello", "", null, 42, undefined];
const truthyOnly = mixed.filter(Boolean);
console.log(truthyOnly); // ["Hello", 42]
filter()
= keep only elements that match a condition.
Perfect for searching, cleaning, or narrowing down arrays.
Often used together with map()
for data transformation.
Array reduce
reduce()
reduces an array to a single value.
It applies a function (called the reducer) to each element, carrying an accumulator along the way.
Very flexible: can be used for sums, averages, flattening arrays, grouping, etc.
array.reduce(callback(accumulator, currentValue, index, array), initialValue);
accumulator
→ result carried over between iterations.
currentValue
→ current element.
index
(optional) → index of current element.
array
(optional) → the whole array.
initialValue
(optional but recommended) → starting value of the accumulator.
const numbers = [1, 2, 3, 4];
const sum = numbers.reduce((acc, num) => acc + num, 0);
console.log(sum); // 10
Explanation:
- Start with
acc = 0
- Step 1:
0 + 1 = 1
- Step 2:
1 + 2 = 3
- Step 3:
3 + 3 = 6
- Step 4:
6 + 4 = 10
//Example: Find Max
const nums = [10, 45, 2, 99, 32];
const max = nums.reduce((acc, num) => (num > acc ? num : acc), nums[0]);
console.log(max); // 99
//Example: Flatten an Array
const nested = [[1, 2], [3, 4], [5]];
const flat = nested.reduce((acc, arr) => acc.concat(arr), []);
console.log(flat); // [1, 2, 3, 4, 5]
//Example: Count Occurrences
const fruits = ["apple", "banana", "apple", "orange", "banana", "apple"];
const counts = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(counts);
// { apple: 3, banana: 2, orange: 1 }
//Example: Average
const scores = [80, 90, 100];
const avg = scores.reduce((acc, score, _, arr) => acc + score / arr.length, 0);
console.log(avg); // 90
reduce()
= turn an array into one value.
Common uses: sum, max/min, flattening, grouping, counting, averages.
Looks harder at first, but it’s one of the most powerful tools in JavaScript.
Array sort
sort()
is an array method that sorts the elements of an array in place (it modifies the original array).
array.sort([compareFunction])
compareFunction
(optional) → defines sort order.
const numbers = [10, 2, 30, 25];
console.log(numbers.sort());
// [10, 2, 25, 30] ❌ (lexicographic order!)
//Problem: sort() converts numbers to strings by default.
// Ascending
numbers.sort((a, b) => a - b);
console.log(numbers); // [2, 10, 25, 30]
// Descending
numbers.sort((a, b) => b - a);
console.log(numbers); // [30, 25, 10, 2]
Compare function logic:
a - b < 0
→a
comes beforeb
a - b > 0
→b
comes beforea
//Sorting Strings
const names = ["Alice", "Bob", "Carol", "anna"];
names.sort();
console.log(names); // ["Alice", "Bob", "Carol", "anna"] (capital letters first)
names.sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
console.log(names); // ["Alice", "anna", "Bob", "Carol"]
//Sorting Objects
const users = [
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 },
{ name: "Carol", age: 20 }
];
// Sort by age ascending
users.sort((a, b) => a.age - b.age);
console.log(users);
/*
[
{ name: "Carol", age: 20 },
{ name: "Alice", age: 25 },
{ name: "Bob", age: 30 }
]
*/
sort()
= rearrange array elements.
Default: lexicographic sort → may be unexpected for numbers.
Use a compare function for numbers or custom rules.
Mutates the original array → use [...array].sort()
if you want a copy.
Working With Immutable Array
What is an Immutable Array?
This is very important in React or functional programming to avoid unintended side effects.
Immutable means you do not change the original array.
Instead of modifying it (like push
, pop
, splice
), you create a new array with the desired changes.
Why Use Immutable Arrays?
- Prevent bugs caused by accidental mutations
- Make state management predictable
- Enable undo/redo or time-travel debugging
//Adding Elements
const arr = [1, 2, 3];
// Add to the end
const newArr1 = [...arr, 4];
console.log(newArr1); // [1, 2, 3, 4]
// Add to the start
const newArr2 = [0, ...arr];
console.log(newArr2); // [0, 1, 2, 3]
//Removing Elements
const arr = [1, 2, 3, 4];
// Remove element with filter
const newArr = arr.filter(num => num !== 2);
console.log(newArr); // [1, 3, 4]
//Updating Elements
const arr = [1, 2, 3, 4];
// Increment all numbers by 1
const newArr = arr.map(num => num + 1);
console.log(newArr); // [2, 3, 4, 5]
//Combining Arrays
const arr1 = [1, 2];
const arr2 = [3, 4];
const combined = [...arr1, ...arr2];
console.log(combined); // [1, 2, 3, 4]
Immutable arrays = never modify the original array
Always return a new array for additions, deletions, or updates
Essential in React state updates and functional programming
Asynchronous Promise
A Promise is a JavaScript object that represents the eventual completion or failure of an asynchronous operation.
- Think of it as a placeholder for a value that will be available in the future.
- Promises help avoid callback hell and make async code easier to read.
A Promise has three states:
- Pending – initial state, operation not finished yet
- Fulfilled – operation completed successfully
- Rejected – operation failed
const myPromise = new Promise((resolve, reject) => {
const success = true; // simulate async operation
if (success) {
resolve("Operation succeeded!");
} else {
reject("Operation failed!");
}
});
resolve(value)
→ fulfills the promise
reject(error)
→ rejects the promise
myPromise
.then(result => {
console.log(result); // "Operation succeeded!"
})
.catch(error => {
console.log(error);
});
.then()
→ runs if promise is fulfilled
.catch()
→ runs if promise is rejected
new Promise((resolve, reject) => {
resolve(2);
})
.then(num => num * 2)
.then(num => num + 1)
.then(result => console.log(result)); // 5
Each .then()
receives the result of the previous one
Makes async code readable and sequential
function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
const data = { id: 1, name: "Alice" };
resolve(data);
}, 1000);
});
}
fetchData()
.then(data => console.log(data))
.catch(err => console.error(err));
Promise.all()
and Promise.race()
Promise.all([p1, p2, ...])
→ waits for all promises to resolvePromise.race([p1, p2, ...])
→ resolves/rejects as soon as one finishes
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
Promise.all([p1, p2]).then(results => console.log(results)); // [1, 2]
Promise = object representing future result of async operation
States: pending
, fulfilled
, rejected
Use .then()
for success, .catch()
for errors
Chain promises for sequential operations
Useful for API calls, file reading, timers, etc.
Asynchronous Async/Await
async
and await
are syntactic sugar on top of Promises.
They allow you to write asynchronous code that looks synchronous, making it easier to read and maintain.
async function greet() {
return "Hello!";
}
greet().then(msg => console.log(msg)); // Hello!
Declaring a function with async
makes it always return a Promise.
Even if you return a normal value ("Hello!"
), it’s automatically wrapped in a Promise.
function wait(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function run() {
console.log("Start");
await wait(1000); // wait 1 second
console.log("End");
}
run();
// Start
// (1 second delay)
// End
await
pauses execution until the Promise resolves.
Can only be used inside async
functions.
async function fetchUser() {
try {
const response = await fetch("https://jsonplaceholder.typicode.com/users/1");
const data = await response.json();
console.log(data);
} catch (err) {
console.error("Error:", err);
}
}
fetchUser();
Sequential (waits one by one):
async function sequential() {
const a = await Promise.resolve(1);
const b = await Promise.resolve(2);
console.log(a + b); // 3
}
Parallel (run at the same time):
async function parallel() {
const [a, b] = await Promise.all([
Promise.resolve(1),
Promise.resolve(2)
]);
console.log(a + b); // 3
}
async
→ marks a function as asynchronous (returns a Promise)
await
→ waits for a Promise to resolve inside an async
function
Use try/catch
for error handling
Can combine with Promise.all()
for parallel operations