Skip to content

Commit e04fc41

Browse files
committed
Coding Excerise
1 parent b250c57 commit e04fc41

30 files changed

Lines changed: 1501 additions & 0 deletions
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function foo() {
2+
let x = (y = 0); // ⚠️ y becomes a global variable
3+
x++;
4+
y++;
5+
return x;
6+
}
7+
8+
console.log(foo(), typeof x, typeof y);
9+
// Output: 1, "undefined", "number"
10+
11+
/**
12+
* Explanation:
13+
*
14+
* 1. `x` is declared using `let`, so it is scoped to the `foo` function.
15+
*
16+
* 2. `y` is NOT declared using `let`, `var`, or `const`.
17+
* This causes `y` to become a global variable.
18+
*
19+
* 3. The expression `x = (y = 0)` is evaluated right to left:
20+
* - `y` is assigned `0` (global)
21+
* - `x` is assigned the value of `y` (local)
22+
*
23+
* 4. `x++` increments the local variable `x` → 1
24+
*
25+
* 5. `y++` increments the global variable `y` → 1
26+
*
27+
* 6. The function returns `x`, which is `1`.
28+
*
29+
* Outside the function:
30+
* - `typeof x === "undefined"` (x is function-scoped)
31+
* - `typeof y === "number"` (y is global)
32+
*/
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
2+
3+
const result = numbers
4+
.filter(num => num % 2 === 0) // [2, 4, 6, 8, 10]
5+
.map(num => num * 2) // [4, 8, 12, 16, 20]
6+
.reduce((sum, num) => sum + num, 0); // 60
7+
8+
console.log(result); // 60
9+
10+
/**
11+
* Explanation:
12+
*
13+
* This demonstrates method chaining with array methods in JavaScript.
14+
*
15+
* 1. filter() creates a new array with elements that pass the test.
16+
* Here, it keeps only even numbers: [2, 4, 6, 8, 10]
17+
*
18+
* 2. map() creates a new array by transforming each element.
19+
* Here, it doubles each number: [4, 8, 12, 16, 20]
20+
*
21+
* 3. reduce() reduces the array to a single value.
22+
* Here, it sums all numbers: 4 + 8 + 12 + 16 + 20 = 60
23+
*
24+
* 4. Each method returns a new array (except reduce), allowing chaining.
25+
*
26+
* 5. This functional programming style is concise and readable.
27+
*
28+
* Note: Each method creates a new array, so for large datasets, consider
29+
* performance implications. A single loop might be more efficient.
30+
*/
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
async function fetchData() {
2+
throw new Error('Network error');
3+
}
4+
5+
async function getData() {
6+
try {
7+
const data = await fetchData();
8+
console.log(data);
9+
return data;
10+
} catch (error) {
11+
console.log('Caught:', error.message);
12+
return null;
13+
}
14+
}
15+
16+
getData().then(result => {
17+
console.log('Result:', result);
18+
});
19+
20+
// Output:
21+
// Caught: Network error
22+
// Result: null
23+
24+
/**
25+
* Explanation:
26+
*
27+
* This demonstrates error handling with async/await in JavaScript.
28+
*
29+
* 1. fetchData() is an async function that throws an error.
30+
*
31+
* 2. In getData(), we use try/catch to handle errors from await fetchData().
32+
*
33+
* 3. When fetchData() throws an error, it's caught by the catch block,
34+
* which logs the error message and returns null.
35+
*
36+
* 4. The .then() receives the return value from getData(), which is null
37+
* because the error was caught and handled.
38+
*
39+
* 5. Without try/catch, the error would propagate and could be caught
40+
* with .catch() on the promise chain.
41+
*/
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
class Rectangle {
2+
constructor(height, width) {
3+
this.height = height;
4+
this.width = width;
5+
}
6+
7+
constructor(width) {
8+
this.width = width;
9+
}
10+
// Getter
11+
get area() {
12+
return this.calcArea();
13+
}
14+
// Method
15+
calcArea() {
16+
return this.height * this.width;
17+
}
18+
}
19+
20+
const square = new Rectangle(20, 30);
21+
22+
console.log(square.area); // Uncaught SyntaxError: A class may only have one constructor

coding-exercise/closure-counter.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
function createCounter() {
2+
let count = 0;
3+
return function() {
4+
count++;
5+
return count;
6+
};
7+
}
8+
9+
const counter1 = createCounter();
10+
const counter2 = createCounter();
11+
12+
console.log(counter1()); // 1
13+
console.log(counter1()); // 2
14+
console.log(counter2()); // 1
15+
console.log(counter1()); // 3
16+
17+
/**
18+
* Explanation:
19+
*
20+
* This demonstrates closure behavior in JavaScript.
21+
*
22+
* 1. createCounter() returns a function that has access to the 'count' variable
23+
* from its outer scope, even after createCounter() has finished executing.
24+
*
25+
* 2. Each call to createCounter() creates a new closure with its own 'count' variable.
26+
* So counter1 and counter2 maintain separate counts.
27+
*
28+
* 3. counter1() increments its own count: 1, 2, 3
29+
* counter2() has its own separate count: 1
30+
*
31+
* This is a classic example of how closures can be used to create private variables.
32+
*/
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Debounce Function
2+
3+
## Challenge
4+
Implement a debounce function that delays the execution of a callback until after a specified delay period has elapsed since the last time it was invoked.
5+
6+
## Problem Description
7+
Debouncing is a programming practice used to ensure that time-consuming tasks do not fire so often. It limits the rate at which a function can fire.
8+
9+
### Real-World Use Cases
10+
- **Search Input**: Wait for the user to stop typing before making an API call
11+
- **Window Resize**: Wait for resize to finish before recalculating layout
12+
- **Scroll Events**: Reduce the number of scroll event handlers fired
13+
- **Button Clicks**: Prevent multiple form submissions
14+
15+
## Example
16+
17+
### Input
18+
```js
19+
function handleSearch(query) {
20+
console.log(`Searching for: ${query}`);
21+
}
22+
23+
const debouncedSearch = debounce(handleSearch, 500);
24+
25+
// User types rapidly
26+
debouncedSearch('J');
27+
debouncedSearch('Ja');
28+
debouncedSearch('Jav');
29+
debouncedSearch('JavaScript');
30+
```
31+
32+
### Output
33+
```
34+
// Only executes once after 500ms of the last call
35+
Searching for: JavaScript
36+
```
37+
38+
## Requirements
39+
1. The debounce function should accept a function and a delay time
40+
2. It should return a new function that delays invoking the original function
41+
3. Each new call should reset the delay timer
42+
4. Only the last call should execute after the delay period
43+
5. The function should preserve the correct `this` context and arguments
44+
45+
## Key Concepts
46+
- **Closures**: Maintaining state (timeoutId) across function calls
47+
- **Higher-Order Functions**: Returning a function from a function
48+
- **setTimeout/clearTimeout**: Managing asynchronous delays
49+
- **Function Context**: Using `apply()` to preserve `this` binding
50+
51+
## Difference from Throttling
52+
- **Debounce**: Executes the function only after the calls have stopped for a specified period
53+
- **Throttle**: Executes the function at most once per specified time interval
54+
55+
## Benefits
56+
- Improves performance by reducing unnecessary function calls
57+
- Reduces API calls and server load
58+
- Provides better user experience by preventing excessive updates
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/**
2+
* Creates a debounced version of a function that delays its execution
3+
* until after a specified delay has elapsed since the last time it was invoked.
4+
*
5+
* @param {Function} func - The function to debounce
6+
* @param {number} delay - The delay in milliseconds to wait before executing the function
7+
* @returns {Function} A debounced version of the original function
8+
*
9+
* @example
10+
* const debouncedFn = debounce(() => console.log('Hello'), 300);
11+
* debouncedFn(); // Won't execute immediately
12+
* debouncedFn(); // Resets the timer
13+
* debouncedFn(); // Only this call will execute after 300ms
14+
*/
15+
function debounce(func, delay) {
16+
// Store the timeout ID in closure scope
17+
// This variable persists across multiple calls to the returned function
18+
let timeoutId;
19+
20+
// Return a new function that wraps the original function
21+
// ...args collects all arguments passed to this function
22+
return function(...args) {
23+
// Clear the previous timeout if it exists
24+
// This prevents the previous scheduled execution from running
25+
// Each new call "resets the timer"
26+
clearTimeout(timeoutId);
27+
28+
// Schedule a new timeout to execute the function after the delay
29+
// This creates a new timer that will execute after 'delay' milliseconds
30+
timeoutId = setTimeout(() => {
31+
// Execute the original function with the correct context and arguments
32+
// func.apply(this, args) ensures:
33+
// 1. 'this' context is preserved (important for object methods)
34+
// 2. All arguments are passed to the original function
35+
func.apply(this, args);
36+
}, delay);
37+
};
38+
}
39+
40+
// ============================================
41+
// EXAMPLE USAGE: Search Input Handler
42+
// ============================================
43+
44+
/**
45+
* Simulates a search API call handler
46+
* In a real application, this would make an HTTP request to a search API
47+
*
48+
* @param {string} query - The search query string
49+
*/
50+
function handleSearch(query) {
51+
console.log(`Searching for: ${query}`);
52+
// In production, this would be something like:
53+
// fetch(`/api/search?q=${query}`).then(...)
54+
}
55+
56+
// Create a debounced version of handleSearch with 500ms delay
57+
// This means the actual search will only execute 500ms after the user stops typing
58+
const debouncedSearch = debounce(handleSearch, 500);
59+
60+
// ============================================
61+
// DEMONSTRATION: Simulating Rapid User Input
62+
// ============================================
63+
64+
console.log('User types rapidly...');
65+
66+
// Each call below happens almost instantly (simulating fast typing)
67+
// But only the LAST call will actually execute after 500ms
68+
debouncedSearch('J'); // Timer starts, will be cancelled
69+
debouncedSearch('Ja'); // Previous timer cancelled, new timer starts
70+
debouncedSearch('Jav'); // Previous timer cancelled, new timer starts
71+
debouncedSearch('Java'); // Previous timer cancelled, new timer starts
72+
debouncedSearch('JavaS'); // Previous timer cancelled, new timer starts
73+
debouncedSearch('JavaSc'); // Previous timer cancelled, new timer starts
74+
debouncedSearch('JavaScr'); // Previous timer cancelled, new timer starts
75+
debouncedSearch('JavaScript'); // Previous timer cancelled, final timer starts
76+
77+
console.log('Waiting 500ms...');
78+
// Only the last call will execute after 500ms delay
79+
// Expected output after 500ms: "Searching for: JavaScript"
80+
//
81+
// Without debouncing, handleSearch would have been called 8 times!
82+
// With debouncing, it's called only once - saving 7 unnecessary API calls
83+
84+
/**
85+
* Explanation:
86+
*
87+
* This demonstrates the debounce pattern in JavaScript.
88+
*
89+
* 1. Debouncing delays the execution of a function until after a certain
90+
* amount of time has passed since it was last called.
91+
*
92+
* 2. When debouncedSearch is called multiple times in quick succession,
93+
* only the LAST call will execute after the delay period.
94+
*
95+
* 3. Each new call clears the previous timeout and sets a new one.
96+
*
97+
* 4. Common use cases:
98+
* - Search input: Wait for user to stop typing before making API call
99+
* - Window resize: Wait for resize to finish before recalculating layout
100+
* - Scroll events: Reduce the number of scroll event handlers fired
101+
*
102+
* 5. Benefits:
103+
* - Reduces unnecessary function calls
104+
* - Improves performance
105+
* - Reduces API calls and server load
106+
*
107+
* 6. The function uses closures to maintain the timeoutId across calls.
108+
*
109+
* 7. func.apply(this, args) ensures the original function is called with
110+
* the correct context and arguments.
111+
*
112+
* Note: This is different from throttling, which executes the function
113+
* at regular intervals regardless of how many times it's called.
114+
*/

0 commit comments

Comments
 (0)