diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..5008ddf Binary files /dev/null and b/.DS_Store differ diff --git a/README.md b/README.md index 78d1bbe..b671300 100644 --- a/README.md +++ b/README.md @@ -6,76 +6,82 @@ You may also check out my other repository - [Learn Node.js - The Hard Way](http ## Table of Contents -1. [Variables](chapters/00_variables.js) -2. [Data Types](chapters/01_data_types.js) -3. [Type Conversion](chapters/02_type_conversion.js) -4. [Type Coercion](chapters/03_type_coercion.js) -5. [Operators](chapters/04_operators.js) -6. [Control Flow](chapters/05_control_flow.js) -7. [Loops](chapters/06_loops.js) -8. [Arrays](chapters/07_arrays.js) -9. [Strings](chapters/08_strings.js) -10. [Functions](chapters/09_functions.js) -11. [Scope](chapters/10_scope.js) -12. [Closure](chapters/11_closure.js) -13. [Objects](chapters/12_objects.js) -14. [Inheritance in Objects](chapters/13_inheritance_objects.js) -15. [Classes](chapters/14_classes.js) -16. [Inheritance in Classes](chapters/15_inheritance_classes.js) -17. [Destructuring](chapters/16_destructuring.js) -18. [Spread and Rest](chapters/17_spread_rest.js) -19. [This](chapters/18_this.js) -20. [Call, Apply, and Bind](chapters/19_call_apply_bind.js) -21. [Error Handling](chapters/20_error_handling.js) -22. [Debugging](chapters/21_debugging.js) -23. [Callbacks](chapters/22_callbacks.js) -24. [Promises](chapters/23_promises.js) -25. [Asynchronous Programming](chapters/24_asynchronous.js) -26. [DOM Manipulation](chapters/25_dom_manipulation.js) -27. [Events](chapters/26_events.js) -28. [Storage](chapters/27_storage.js) -29. [IndexedDB](chapters/28_indexed_db.js) -30. [Symbols](chapters/29_symbol.js) -31. [Fetch API](chapters/30_fetch.js) -32. [Modules](chapters/31_modules.js) -33. [Template Literals](chapters/32_template_literals.js) -34. [Date and Time](chapters/33_date_time.js) -35. [Math](chapters/34_math.js) -36. [Bitwise Operations](chapters/35_bitwise.js) -37. [Regular Expressions](chapters/36_regex.js) -38. [Performance Optimization](chapters/48_performance.js) -39. [Navigator API](chapters/49_navigator.js) -40. [User Timing API](chapters/50_user_timing_api.js) -41. [Navigation Timing API](chapters/51_navigation_timing.js) +1. [Variables](./chapters/00_variables.js) +2. [Data Types](./chapters/01_data_types.js) +3. [Type Conversion](./chapters/02_type_conversion.js) +4. [Type Coercion](./chapters/03_type_coercion.js) +5. [Operators](./chapters/04_operators.js) + - [Ternary Operator](./chapters/04.1_ternary.js) + - [Nullish Coalescing](./chapters/04.2_nullish_coalescing.js) +6. [Control Flow](./chapters/05_control_flow.js) +7. [Loops](./chapters/06_loops.js) +8. [Arrays](./chapters/07_arrays.js) +9. [Strings](./chapters/08_strings.js) +10. [Functions](./chapters/09_functions.js) +11. [Scope](./chapters/10_scope.js) +12. [Objects](./chapters/12_objects.js) + - [Error Object](./chapters/12.1_error_object.js) + - [Optional Chaining](./chapters/12.1_optional_chaining.js) +13. [Inheritance with Objects](./chapters/13_inheritance_objects.js) +14. [Classes](./chapters/14_classes.js) +15. [Inheritance with Classes](./chapters/15_inheritance_classes.js) +16. [Destructuring](./chapters/16_destructuring.js) +17. [Spread and Rest Operators](./chapters/17_spread_rest.js) +18. [`this` Keyword](./chapters/18_this.js) +19. [Call, Apply, Bind](./chapters/19_call_apply_bind.js) +20. [Error Handling](./chapters/20_error_handling.js) +21. [Debugging](./chapters/21_debugging.js) +22. [Callbacks](./chapters/22_callbacks.js) +23. [Promises](./chapters/23_promises.js) + - [Async/Await](./chapters/23.1_async_await.js) +24. [Asynchronous Programming](./chapters/24_asynchronous.js) +25. [DOM Manipulation](./chapters/25_dom_manipulation.js) +26. [Events](./chapters/26_events.js) +27. [Storage](./chapters/27_storage.js) +28. [IndexedDB](./chapters/28_indexed_db.js) +29. [Symbol](./chapters/29_symbol.js) +30. [Fetch API](./chapters/30_fetch.js) +31. [Modules](./chapters/31_modules.js) +32. [Template Literals](./chapters/32_template_literals.js) +33. [Date and Time](./chapters/33_date_time.js) +34. [Math](./chapters/34_math.js) +35. [Bitwise Operations](./chapters/35_bitwise.js) +36. [Regular Expressions](./chapters/36_regex.js) +37. [`setTimeout`](./chapters/37_set_timeout.js) +38. [`setInterval`](./chapters/38_setInterval.js) +39. [`JSON.stringify`](./chapters/39_json_stringify.js) +40. [`JSON.parse`](./chapters/40_json_parse.js) +41. [Map](./chapters/41_map.js) +42. [WeakMap](./chapters/42_weak_map.js) +43. [Set](./chapters/43_set.js) +44. [WeakSet](./chapters/44_weak_map.js) +45. [Generators](./chapters/45_generators.js) +46. [Iterators](./chapters/46_iterators.js) +47. [BigInt](./chapters/47_big_int.js) +48. [Web APIs](./chapters/48.0_web_apis.js) + - [Web APIs 2](./chapters/48.1_web_apis_2.js) + - [Web APIs 3](./chapters/48.2_web_apis_3.js) +49. [Canvas](./chapters/49_canvas.js) +50. [Drag and Drop](./chapters/50_drag_drop.js) +51. [File and Blob](./chapters/51_file_and_blob.js) +52. [Websockets](./chapters/52_websockets.js) +53. [Web Workers](./chapters/53_web_workers.js) +54. [Service Workers](./chapters/54_service_workers.js) +55. [Custom Events](./chapters/55_custom_events.js) +56. [WebRTC](./chapters/56_webrtc.js) +57. [Dynamic Imports](./chapters/57_dynamic_imports.js) +58. [Decorators](./chapters/58_decorators.js) +59. [Proxy](./chapters/59_proxy.js) +60. [Reflect](./chapters/60_reflect.js) +61. [Performance](./chapters/61_performance.js) +62. [Navigator](./chapters/62_navigator.js) +63. [User Timing API](./chapters/63_user_timing_api.js) +64. [Navigation Timing](./chapters/64_navigation_timing.js) +65. [Lazy Loading](./chapters/65_lazy_loading.js) ## Additional Topics (To Be Added) -- `setTimeout()` and `setInterval()` -- `clearTimeout()` and `clearInterval()` -- `JSON.stringify()` and `JSON.parse()` -- `Map()` -- `Set()` -- `WeakMap()` and `WeakSet()` -- Generators -- Iterators -- `async/await` -- BigInt -- Web APIs (Window, Document) -- Canvas API -- Drag and Drop API -- File and Blob -- WebSockets -- Web Workers -- Service Workers -- Custom Events -- WebRTC -- LocalStorage, SessionStorage, and Cookies -- FormData -- Dynamic Import -- Decorators -- Proxy -- Reflect -- Memory Management +There are more complex topics that I would like to cover in this repository. I will be adding them as I get time to write them. Most of them will be related to performance and optimization. ## More chapters (on demand) diff --git a/chapters/04.1_ternary.js b/chapters/04.1_ternary.js new file mode 100644 index 0000000..ec7910b --- /dev/null +++ b/chapters/04.1_ternary.js @@ -0,0 +1,82 @@ +/** + * ======================================================== + * Conditional (Ternary) Operator + * ======================================================== + * The conditional (ternary) operator is a shorthand way to perform conditional (if-else-like) operations. + * The syntax is: condition ? expression1 : expression2 + * If the condition is true, expression1 is executed, otherwise, expression2 is executed. + */ + +/** + * ======================================================== + * Basic Usage + * ======================================================== + * In a simple example, you can use it to assign a value to a variable based on a condition. + */ +const isAdult = true; +const type = isAdult ? "Adult" : "Minor"; +console.log(`Person is an: ${type}`); // Output: 'Person is an: Adult' + +/** + * ======================================================== + * Nested Ternary Operators + * ======================================================== + * Ternary operators can be nested for multiple conditions, but this may reduce readability. + */ +const age = 25; +const ageGroup = age < 18 ? "Minor" : age < 60 ? "Adult" : "Senior"; +console.log(`Age Group: ${ageGroup}`); // Output: 'Age Group: Adult' + +/** + * ======================================================== + * Using with Functions + * ======================================================== + * You can also execute functions using the ternary operator. + */ +function greetMorning() { + return "Good Morning"; +} + +function greetEvening() { + return "Good Evening"; +} + +const isMorning = true; +console.log(isMorning ? greetMorning() : greetEvening()); // Output: 'Good Morning' + +/** + * ======================================================== + * As an Expression + * ======================================================== + * The ternary operator is an expression, meaning it returns a value. + * You can use it inline with other operations. + */ +const score = 95; +console.log(`You ${score > 50 ? "passed" : "failed"} the exam.`); // Output: 'You passed the exam.' + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. Type Coercion + * ---------------- + * Be cautious about type coercion when using the ternary operator, as it follows the same rules as other JavaScript operators. + */ +const value = "5"; +const number = value == 5 ? "Loose equality" : "Strict inequality"; +console.log(`Type Coercion: ${number}`); // Output: 'Loose equality' + +/** + * 2. Avoid Side Effects + * ---------------------- + * Avoid using ternary operators for operations that produce side effects, like assignments or function calls with side effects. + */ + +/** + * 3. Readability + * -------------- + * While chaining or nesting ternary operators can be powerful, it can also make code harder to read and maintain. + */ diff --git a/chapters/04.2_nullish_coalescing.js b/chapters/04.2_nullish_coalescing.js new file mode 100644 index 0000000..e69de29 diff --git a/chapters/09_functions.js b/chapters/09_functions.js index 3779935..3126ddb 100644 --- a/chapters/09_functions.js +++ b/chapters/09_functions.js @@ -9,7 +9,7 @@ function functions() { function sayHello(name) { return `Hello, ${name}`; } - console.log(sayHello("Alice")); // Outputs: "Hello, Alice" + console.log(sayHello("Ishtmeet")); // Outputs: "Hello, Ishtmeet" /** * ======================================================== diff --git a/chapters/12.1_error_object.js b/chapters/12.1_error_object.js new file mode 100644 index 0000000..2a7df8c --- /dev/null +++ b/chapters/12.1_error_object.js @@ -0,0 +1,188 @@ +/** + * ======================================================== + * Error Object in JavaScript + * ======================================================== + * The Error object is a built-in object in JavaScript that provides information about errors that occur while + * a script is running. It's a way to handle both custom and system-generated exceptions. + */ + +/** + * ======================================================== + * Creating a Simple Error + * ======================================================== + * You can create an instance of the Error object using the Error constructor. + */ + +const simpleError = new Error("Something went wrong"); + +/** + * The Error object contains a message property that describes the error. The above code creates a new error object + * with the message "Something went wrong". + */ + +/** + * ======================================================== + * Throwing Errors + * ======================================================== + * Throwing errors halts the normal execution of code and directs the flow to the nearest catch block. + */ + +function throwError() { + throw new Error("An error occurred"); +} + +try { + throwError(); +} catch (error) { + console.error(error.message); +} + +/** + * Here, the function 'throwError' throws an error, which is caught by the catch block. The message is then logged + * to the console. + */ + +/** + * ======================================================== + * Handling Errors with Try-Catch + * ======================================================== + * The try-catch statement allows you to handle errors gracefully without breaking your entire code. + */ + +try { + // Potentially error-prone code + const sum = add(5, "ten"); +} catch (error) { + console.error("An error occurred:", error.message); +} + +/** + * The 'add' function is supposed to be a numerical addition, but a string is passed instead. This would typically + * result in an error, but the catch block catches it and prints the error message. + */ + +/** + * ======================================================== + * Custom Errors + * ======================================================== + * You can create custom error types by extending the Error class to handle specific kinds of errors. + */ + +class ValidationError extends Error { + constructor(message) { + super(message); + this.name = "ValidationError"; + } +} + +// Usage +try { + throw new ValidationError("Invalid input"); +} catch (error) { + if (error instanceof ValidationError) { + console.error(`Custom error caught: ${error.message}`); + } else { + console.error(`Unknown error: ${error.message}`); + } +} + +/** + * The custom 'ValidationError' class extends the native Error class. It can be thrown and caught just like any + * other error but allows for more specific error handling. + */ + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. Stack Trace + * --------------- + * The stack trace is a property of the Error object that provides information about the function calls leading up to + * the error. + */ + +try { + throw new Error("An error occurred"); +} catch (error) { + console.error(error.stack); +} + +/** + * Output: This will display the error message along with the function calls that led to this error. + * It can be immensely helpful for debugging. + */ + +/** + * 2. Catching Multiple Error Types + * --------------------------------- + * You can catch and handle multiple types of errors using instanceof in a catch block. + */ + +try { + // Potentially error-prone code +} catch (error) { + if (error instanceof TypeError) { + console.error("Type Error:", error.message); + } else if (error instanceof ReferenceError) { + console.error("Reference Error:", error.message); + } +} + +/** + * Output: Depending on the type of error thrown, a specific error message will be logged. + */ + +/** + * 3. Error Handling with Promises + * ------------------------------- + * Errors can be caught in a Promise chain using the 'catch' method. + */ + +fetch("invalid_url") + .then((response) => response.json()) + .catch((error) => console.error("Fetch Error:", error.message)); + +/** + * Output: 'Fetch Error:' followed by the error message will be displayed, indicating that the fetch operation failed. + */ + +/** + * 4. Finally Block + * ----------------- + * The 'finally' block will be executed regardless of whether an error was thrown or not. + */ + +try { + // Code that may throw an error +} catch (error) { + console.error(error.message); +} finally { + console.log("Cleanup code"); +} + +/** + * Output: If an error occurs, the error message will be logged. The 'Cleanup code' message will always be logged + * afterwards. + */ + +/** + * 5. Async/Await Error Handling + * ----------------------------- + * With async/await, you can use try/catch just like you would with synchronous code. + */ + +async function fetchData() { + try { + const response = await fetch("some_url"); + const data = await response.json(); + } catch (error) { + console.error("Fetch Error:", error.message); + } +} + +/** + * Output: If an error occurs during the fetch operation, 'Fetch Error:' followed by the error message will be logged. + */ diff --git a/chapters/12.1_optional_chaining.js b/chapters/12.1_optional_chaining.js new file mode 100644 index 0000000..c25fbb2 --- /dev/null +++ b/chapters/12.1_optional_chaining.js @@ -0,0 +1,89 @@ +/** + * ======================================================== + * Optional Chaining (?.) + * ======================================================== + * The optional chaining operator (?.) permits reading the value of a property located deep within a chain + * of connected objects without having to expressly validate that each reference in the chain is valid. + * It short-circuits if it encounters a null or undefined, returning undefined as the evaluation result. + */ + +/** + * ======================================================== + * Basic Usage + * ======================================================== + * Optional chaining is used to safely access deeply nested properties of an object. + */ +const user = { + profile: { + name: "John", + age: 30, + }, +}; + +const userName = user?.profile?.name; +console.log(`User Name: ${userName}`); // Output: 'User Name: John' + +/** + * ======================================================== + * Accessing Array Items + * ======================================================== + * You can use optional chaining when attempting to access an index of an array that might be undefined. + */ +const arr = [1, 2, 3]; +const value = arr?.[4]; +console.log(`Array Value: ${value}`); // Output: 'Array Value: undefined' + +/** + * ======================================================== + * Function or Method Calls + * ======================================================== + * You can also use optional chaining when calling a function or method that might not exist. + */ +const greet = user?.profile?.greet?.(); +console.log(`Greeting: ${greet}`); // Output: 'Greeting: undefined' + +/** + * ======================================================== + * Combined with Nullish Coalescing + * ======================================================== + * Optional chaining can be combined with the nullish coalescing operator for setting defaults. + */ +const city = user?.profile?.address?.city ?? "Unknown"; +console.log(`City: ${city}`); // Output: 'City: Unknown' + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ +/** + * 1. Short-Circuiting + * -------------------- + * If optional chaining encounters null or undefined, it short-circuits the rest of the evaluation. + */ +const obj = { prop: null }; +const shortCircuit = obj?.prop?.nonExistentMethod?.(); +console.log(`Short-Circuit Result: ${shortCircuit}`); // Output: 'Short-Circuit Result: undefined' + +/** + * 2. Operator Precedence + * ---------------------- + * To avoid any ambiguity when combining optional chaining with other operators, use parentheses. + */ +const mixed = { value: 5 }; +const ambiguous = mixed?.value + 1; // This will result in 6, as you would expect +const clarified = mixed?.value + 1; // Use parentheses to make the operation order explicit +console.log(`Ambiguous: ${ambiguous}`); // Output: 'Ambiguous: 6' +console.log(`Clarified: ${clarified}`); // Output: 'Clarified: 6' + +/** + * 3. Functionality Limitation + * --------------------------- + * The operator only checks for null or undefined, but not for other falsy values like 0 or ''. + */ +const zeroCheck = { zero: 0 }; +const zeroValue = zeroCheck?.zero ?? "No value"; +console.log(`Zero Value: ${zeroValue}`); // Output: 'Zero Value: 0' + +// const user: User = { id: 1, name: { first: 'John' } }; +// const lastName = user?.name?.last; // No TypeScript error here diff --git a/chapters/12_objects.js b/chapters/12_objects.js index a6787c5..911b6c7 100644 --- a/chapters/12_objects.js +++ b/chapters/12_objects.js @@ -7,13 +7,13 @@ function objects() { * Objects can have properties and methods (functions as properties). */ const person = { - name: "Alice", + name: "Ishtmeet", age: 30, greet() { return `Hello, ${this.name}`; }, }; - console.log(person.greet()); // Outputs: "Hello, Alice" + console.log(person.greet()); // Outputs: "Hello, Ishtmeet" /** * ======================================================== @@ -32,7 +32,7 @@ function objects() { * New properties can be added and existing properties can be updated. * This showcases the dynamic nature of JavaScript objects. */ - person.email = "alice@example.com"; + person.email = "ishtmeet@example.com"; person["age"] = 31; console.log(person); // Outputs updated object @@ -77,7 +77,7 @@ function objects() { * This feature was introduced in ES6 and is often used for better readability. */ const { name, age } = person; - console.log(name, age); // Outputs: "Alice 31" + console.log(name, age); // Outputs: "Ishtmeet 31" /** * ======================================================== diff --git a/chapters/16_destructuring.js b/chapters/16_destructuring.js index 2d7f0f8..afa0f28 100644 --- a/chapters/16_destructuring.js +++ b/chapters/16_destructuring.js @@ -19,8 +19,8 @@ function destructuring() { * ======================================================== * You can provide default values for both array and object destructuring. */ - const { name = "John" } = {}; - console.log(name); // Outputs 'John' + const { name = "Ishtmeet" } = {}; + console.log(name); // Outputs 'Ishtmeet' /** * ======================================================== @@ -57,7 +57,7 @@ function destructuring() { function greet({ name, age }) { console.log(`Hello, ${name}. You are ${age} years old.`); } - greet({ name: "Alice", age: 25 }); // Outputs "Hello, Alice. You are 25 years old." + greet({ name: "Ishtmeet", age: 25 }); // Outputs "Hello, Ishtmeet. You are 25 years old." /** * ======================================================== diff --git a/chapters/18_this.js b/chapters/18_this.js index 0c65d31..78a2f23 100644 --- a/chapters/18_this.js +++ b/chapters/18_this.js @@ -30,9 +30,9 @@ function thisKeyword() { * In a method, which is a function stored as an object property, `this` refers to the object. */ const obj = { - name: "Alice", + name: "Ishtmeet", greet: function () { - console.log(`Hello, my name is ${this.name}`); // Outputs "Hello, my name is Alice" + console.log(`Hello, my name is ${this.name}`); // Outputs "Hello, my name is Ishtmeet" }, }; obj.greet(); @@ -69,8 +69,8 @@ function thisKeyword() { function showName(label) { console.log(`${label}: ${this.name}`); } - const alice = { name: "Alice" }; - showName.call(alice, "User"); // Outputs "User: Alice" + const ishtmeet = { name: "Ishtmeet" }; + showName.call(ishtmeet, "User"); // Outputs "User: Ishtmeet" /** * ======================================================== diff --git a/chapters/19_call_apply_bind.js b/chapters/19_call_apply_bind.js index 05318f4..434261f 100644 --- a/chapters/19_call_apply_bind.js +++ b/chapters/19_call_apply_bind.js @@ -13,7 +13,7 @@ function callApplyBind() { console.log(`${this.title} ${firstname} ${lastname}`); } const person1 = { title: "Mr." }; - showFullName.call(person1, "John", "Doe"); // Outputs: "Mr. John Doe" + showFullName.call(person1, "Ishtmeet", "Doe"); // Outputs: "Mr. Ishtmeet Doe" /** * ======================================================== diff --git a/chapters/20_error_handling.js b/chapters/20_error_handling.js index b21a0a6..ee158b8 100644 --- a/chapters/20_error_handling.js +++ b/chapters/20_error_handling.js @@ -11,7 +11,7 @@ function errorHandling() { */ try { // Code that could possibly throw an error goes here - const x = JSON.parse('{"name": "John"}'); + const x = JSON.parse('{"name": "Ishtmeet"}'); } catch (e) { // Code to execute if an exception occurs in the try block console.error("An error occurred:", e); diff --git a/chapters/23.1_async_await.js b/chapters/23.1_async_await.js new file mode 100644 index 0000000..6d27335 --- /dev/null +++ b/chapters/23.1_async_await.js @@ -0,0 +1,105 @@ +/** + * ======================================================== + * Async/Await + * ======================================================== + * 'async/await' is a modern way to write asynchronous code in JavaScript. + * It provides a more readable and maintainable syntax over callbacks and Promises. + */ + +/** + * ======================================================== + * 1. Basic Syntax + * ======================================================== + * Declaring a function as 'async' makes it return a Promise implicitly. + * You can use 'await' within an 'async' function to pause the execution until a Promise is resolved or rejected. + */ +async function fetchData() { + const response = await fetch("https://api.example.com/data"); + const data = await response.json(); + return data; +} +// Invoke the function to see it in action +fetchData().then((data) => console.log(data)); + +/** + * ======================================================== + * 2. Handling Errors + * ======================================================== + * It's crucial to handle errors when dealing with asynchronous operations. + * 'try/catch' blocks are commonly used within 'async' functions for this purpose. + */ +async function fetchDataWithErrorHandling() { + try { + const response = await fetch("https://api.example.com/data"); + if (!response.ok) { + throw new Error("Network response was not ok"); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Fetch Error:", error); + } +} +// Invoke the function to see error handling in action +fetchDataWithErrorHandling().catch((error) => console.error(error)); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * Error Propagation + * ----------------- + * Throwing an exception within an 'async' function causes it to return a Promise that is rejected. + */ +async function failAsync() { + throw new Error("Failed"); + // This is equivalent to: return Promise.reject(new Error('Failed')); +} + +/** + * Concurrency with Promise.all() + * ------------------------------ + * 'async/await' can work in tandem with Promise.all() to execute multiple asynchronous operations concurrently. + */ +async function fetchAllData() { + const [data1, data2] = await Promise.all([ + fetch("https://api.example.com/data1").then((res) => res.json()), + fetch("https://api.example.com/data2").then((res) => res.json()), + ]); + return { data1, data2 }; +} + +/** + * Using 'for-await-of' with Async Iterables + * ----------------------------------------- + * The 'for-await-of' loop enables you to traverse through async iterables as though they were synchronous. + */ +async function handleAsyncIterable(asyncIterable) { + for await (const item of asyncIterable) { + console.log(item); + } +} + +/** + * Non-Promise Asynchronous Operations + * ---------------------------------- + * While 'await' is designed to work with Promises, it can also be used with non-Promise values. + * When you 'await' a non-Promise value, it's returned instantly. + */ +async function notReallyAsync() { + const value = await 42; + return value; // 42, instantly +} + +/** + * Running Async Functions Immediately + * ----------------------------------- + * You can immediately invoke an async function using an IIFE (Immediately Invoked Function Expression). + */ +(async function () { + const data = await fetchData(); + console.log(data); +})(); diff --git a/chapters/27_storage.js b/chapters/27_storage.js index eb39431..179e7ef 100644 --- a/chapters/27_storage.js +++ b/chapters/27_storage.js @@ -12,9 +12,9 @@ * ======================================================== * localStorage allows you to store data with no expiration time. This data will persist even after closing the browser. */ -localStorage.setItem("username", "JohnDoe"); +localStorage.setItem("username", "IshtmeetDoe"); const username = localStorage.getItem("username"); -console.log(`Username: ${username}`); // Output: Username: JohnDoe +console.log(`Username: ${username}`); // Output: Username: IshtmeetDoe localStorage.removeItem("username"); /** @@ -34,8 +34,8 @@ sessionStorage.removeItem("sessionId"); * ======================================================== * Both localStorage and sessionStorage provide a way to iterate over stored items. */ -localStorage.setItem("username", "JohnDoe"); -localStorage.setItem("email", "john.doe@example.com"); +localStorage.setItem("username", "IshtmeetDoe"); +localStorage.setItem("email", "ishtmeet.doe@example.com"); for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); const value = localStorage.getItem(key); @@ -63,10 +63,10 @@ window.addEventListener("storage", function (event) { * ----------------------- * Both localStorage and sessionStorage can only directly store strings. To store objects or arrays, serialize them to JSON. */ -const user = { name: "JohnDoe", age: 30 }; +const user = { name: "IshtmeetDoe", age: 30 }; localStorage.setItem("user", JSON.stringify(user)); const retrievedUser = JSON.parse(localStorage.getItem("user")); -console.log(retrievedUser); // Output: { name: 'JohnDoe', age: 30 } +console.log(retrievedUser); // Output: { name: 'IshtmeetDoe', age: 30 } /** * 2. Storage Limit diff --git a/chapters/31_modules.js b/chapters/31_modules.js index cc288a8..accc800 100644 --- a/chapters/31_modules.js +++ b/chapters/31_modules.js @@ -67,6 +67,8 @@ import myDefaultFunction from "./myModule.js"; * ------------------ * Dynamic imports enable you to load modules on demand using `import()`. * This returns a promise that resolves into the imported module. + * + * We have a dedicated chapter on dynamic imports. */ const modulePath = "./myModule.js"; import(modulePath) diff --git a/chapters/32_template_literals.js b/chapters/32_template_literals.js index 4f7c62d..651ce31 100644 --- a/chapters/32_template_literals.js +++ b/chapters/32_template_literals.js @@ -1,6 +1,6 @@ /** * ======================================================== - * Template Literals in JavaScript + * Template Literals * ======================================================== */ @@ -16,8 +16,8 @@ const str = `Hello, world!`; // String with variables -const name = "John"; -const greeting = `Hello, ${name}!`; // Output: "Hello, John!" +const name = "Ishtmeet"; +const greeting = `Hello, ${name}!`; // Output: "Hello, Ishtmeet!" // String with expressions const x = 10, @@ -46,7 +46,7 @@ const multiLineStr = ` function myTag(strings, ...values) { console.log(strings); // Outputs: Array of string literals like ["Hello ", ", the sum is ", "."] - console.log(values); // Outputs: Array of evaluated expressions like ["John", 30] + console.log(values); // Outputs: Array of evaluated expressions like ["Ishtmeet", 30] return "Modified String"; } @@ -62,14 +62,14 @@ const tagged = myTag`Hello ${name}, the sum is ${x + y}.`; // Calls myTag functi * 1. Nesting Template Literals */ // Template literals can be nested within another template literal. -const nested = `outer ${`inner ${name}`}`; // Output: "outer inner John" +const nested = `outer ${`inner ${name}`}`; // Output: "outer inner Ishtmeet" /** * 2. Raw Strings * * The String.raw tag function allows you to treat backslashes as literal characters. */ -const rawStr = String.raw`This is a raw string \n ${name}.`; // Output: "This is a raw string \n John" +const rawStr = String.raw`This is a raw string \n ${name}.`; // Output: "This is a raw string \n Ishtmeet" /** * 3. Custom Interpolators diff --git a/chapters/33_date_time.js b/chapters/33_date_time.js index ce314cf..b92b1f5 100644 --- a/chapters/33_date_time.js +++ b/chapters/33_date_time.js @@ -81,7 +81,7 @@ const utcHours = now.getUTCHours(); * * NOTE: Some browsers may return the below as a valid date, setting date to the 00:00:00 Jan 1, 2014 */ -const invalidDate = new Date("john-doe 2014").toString(); +const invalidDate = new Date("ishtmeet-doe 2014").toString(); /** * 4. Leap Year Considerations diff --git a/chapters/34_math.js b/chapters/34_math.js index 70f9b77..815e806 100644 --- a/chapters/34_math.js +++ b/chapters/34_math.js @@ -1,6 +1,6 @@ /* * ======================================================== - * Math Object in JavaScript + * Math Object * ======================================================== * The Math object provides static properties and methods for mathematical constants and functions. * It doesn't need to be instantiated. diff --git a/chapters/37_set_timeout.js b/chapters/37_set_timeout.js new file mode 100644 index 0000000..e8b1e8d --- /dev/null +++ b/chapters/37_set_timeout.js @@ -0,0 +1,85 @@ +function _setTimeout() { + /** + * ======================================================== + * Basic Syntax and Usage of setTimeout + * ======================================================== + * The setTimeout method allows you to schedule code execution after + * a specified delay. The delay is not guaranteed but approximate, due to + * the single-threaded nature of JavaScript. + * + * Syntax: setTimeout(callback, delayInMilliseconds, ...additionalArguments) + * - callback: The function that will be executed after the delay. + * - delayInMilliseconds: The delay in milliseconds. + * - ...additionalArguments: Optional arguments that are passed to the callback. + */ + setTimeout(() => { + console.log("Basic Usage: This message will appear after approximately 1 second."); + }, 1000); + + /** + * ======================================================== + * Canceling a Scheduled Timeout + * ======================================================== + * The setTimeout function returns a TimeoutID, which can be used to cancel + * the timeout using clearTimeout method. + */ + const timeoutID = setTimeout(() => { + console.log("This message will never appear."); + }, 2000); + clearTimeout(timeoutID); + + /** + * ======================================================== + * Passing Parameters to the Callback + * ======================================================== + * setTimeout allows passing additional arguments to the callback function. + * These arguments follow the delay parameter in the function signature. + */ + setTimeout( + (param1, param2) => { + console.log(`Passed Parameters: ${param1}, ${param2}`); + }, + 1500, + "Parameter1", + "Parameter2" + ); + + /** + * ======================================================== + * Handling 'this' Context within setTimeout + * ======================================================== + * If you're using an arrow function as the callback, the value of 'this' + * will be inherited from the enclosing scope. + * + * For regular functions, the 'this' value will be either the window object + * (in a browser) or undefined (in strict mode). + */ + const exampleObject = { + name: "Ishtmeet", + greet: function () { + setTimeout(() => { + console.log(`Hello, ${this.name}`); + }, 500); + }, + }; + exampleObject.greet(); // Output: "Hello, Ishtmeet" + + /** + * ======================================================== + * Nuances and Edge Cases + * ======================================================== + * 1. Zero Delay: Even if you set zero milliseconds as the delay, the actual + * execution may take longer because the callback enters the event queue. + * 2. Maximum Delay: Delay can't exceed 2147483647 milliseconds (~24.8 days). + * Any value longer will be truncated. + */ + setTimeout(() => { + console.log("Zero delay doesn't mean immediate execution."); + }, 0); + + setTimeout(() => { + console.log("This message will display after 1 millisecond, not 25 days."); + }, 2147483648); +} + +_setTimeout(); diff --git a/chapters/38_setInterval.js b/chapters/38_setInterval.js new file mode 100644 index 0000000..77d5c56 --- /dev/null +++ b/chapters/38_setInterval.js @@ -0,0 +1,85 @@ +// Function to explore the intricacies and features of setInterval +function _setInterval() { + /** + * ======================================================== + * Basic Syntax and Usage of setInterval + * ======================================================== + * The setInterval method schedules repeated execution of code + * at specific intervals. Just like setTimeout, the actual intervals are approximate. + * + * Syntax: setInterval(callback, intervalInMilliseconds, ...additionalArguments) + * - callback: The function that will be executed at each interval. + * - intervalInMilliseconds: The interval time in milliseconds. + * - ...additionalArguments: Optional arguments that are passed to the callback. + */ + const basicIntervalID = setInterval(() => { + console.log("Basic Usage: This message will repeat every 2 seconds."); + }, 2000); + + // To stop the above basic example after 6 seconds + setTimeout(() => { + clearInterval(basicIntervalID); + }, 6000); + + /** + * ======================================================== + * Canceling a Scheduled Interval + * ======================================================== + * The setInterval function returns an IntervalID, which can be used to cancel + * the interval using the clearInterval method. + */ + const cancelableIntervalID = setInterval(() => { + console.log("This message will display only once."); + }, 3000); + clearInterval(cancelableIntervalID); + + /** + * ======================================================== + * Passing Parameters to the Callback + * ======================================================== + * setInterval allows passing additional arguments to the callback function. + * These arguments follow the interval parameter in the function signature. + */ + setInterval( + (param1, param2) => { + console.log(`Passed Parameters: ${param1}, ${param2}`); + }, + 4000, + "Parameter1", + "Parameter2" + ); + + /** + * ======================================================== + * Handling 'this' Context within setInterval + * ======================================================== + * The rules for the 'this' context within the callback function are similar + * to those in setTimeout. Arrow functions inherit 'this' from the surrounding + * code, while regular functions don't. + */ + const exampleObject = { + name: "Bob", + greet: function () { + setInterval(() => { + console.log(`Hello, ${this.name}`); + }, 5000); + }, + }; + exampleObject.greet(); // Output: "Hello, Bob" + + /** + * ======================================================== + * Nuances and Edge Cases + * ======================================================== + * 1. Zero Interval: Even with zero milliseconds as the interval, the actual + * execution may not be immediate due to JavaScript's single-threaded nature. + * 2. Maximum Interval: The maximum length is 2147483647 milliseconds (~24.8 days), + * similar to setTimeout. + */ + setInterval(() => { + console.log("Zero interval doesn't mean immediate repetition."); + }, 0); +} + +// Run the exploreSetInterval function to observe the behavior of setInterval +_setInterval(); diff --git a/chapters/39_json_stringify.js b/chapters/39_json_stringify.js new file mode 100644 index 0000000..a37518d --- /dev/null +++ b/chapters/39_json_stringify.js @@ -0,0 +1,96 @@ +function jsonStringify() { + /** + * ======================================================== + * Basic Syntax and Usage of JSON.stringify() + * ======================================================== + * The JSON.stringify() method converts a JavaScript value (object, array, string, + * number, boolean, or null) to a JSON-formatted string. + * + * Syntax: JSON.stringify(value, replacer?, space?) + * - value: The JavaScript value to convert to a JSON string. + * - replacer: Either a function or an array used to filter or modify the results. + * - space: Specifies the indentation for readability. + */ + const person = { + name: "Ishtmeet", + age: 25, + }; + const jsonString = JSON.stringify(person); + console.log(`Basic Usage: ${jsonString}`); // Output: '{"name":"Ishtmeet","age":25}' + + /** + * ======================================================== + * Using Replacer Function + * ======================================================== + * The replacer function can be used to filter out values or to transform the values + * before they get stringified. The replacer function takes two arguments, the 'key' and 'value'. + */ + const replacerFunction = (key, value) => { + if (typeof value === "number") { + return value * 2; + } + return value; + }; + console.log(`With Replacer Function: ${JSON.stringify(person, replacerFunction)}`); // Output: '{"name":"Ishtmeet","age":50}' + + /** + * ======================================================== + * Using Space for Indentation + * ======================================================== + * The 'space' parameter specifies the number of spaces to use for indentation. + * This makes the output JSON string more readable. + */ + const prettyJsonString = JSON.stringify(person, null, 4); + console.log(`Pretty Printed JSON: \n${prettyJsonString}`); + + /** + * ======================================================== + * Nuances and Edge Cases + * ======================================================== + */ + + /** + * Handling Undefined and Functions + * -------------------------------- + * JSON.stringify() will omit properties with undefined values, functions, or Symbol types. + */ + const objWithUndefined = { name: "Ishtmeet", greet: undefined, sayHi: function () {} }; + console.log(`Omitting Undefined and Functions: ${JSON.stringify(objWithUndefined)}`); // Output: '{"name":"Ishtmeet"}' + + /** + * Handling Circular References + * ---------------------------- + * JSON.stringify() throws an error when there are circular references in the object. + */ + const circularObj = { name: "Ishtmeet" }; + circularObj.self = circularObj; + // Uncomment the following line will result in an error + // console.log(JSON.stringify(circularObj)); + + /** + * toJSON Method + * ------------ + * If an object has a toJSON method, JSON.stringify() calls it and stringifies + * the value returned by toJSON(). + */ + const objWithToJSON = { + name: "Ishtmeet", + age: 25, + toJSON() { + return { + name: this.name, + }; + }, + }; + console.log(`Using toJSON method: ${JSON.stringify(objWithToJSON)}`); // Output: '{"name":"Ishtmeet"}' + + /** + * Handling Dates + * -------------- + * JSON.stringify() will convert Date objects to their ISO string representation. + */ + const objWithDate = { name: "Ishtmeet", birthDate: new Date() }; + console.log(`Handling Dates: ${JSON.stringify(objWithDate)}`); +} + +jsonStringify(); diff --git a/chapters/40_json_parse.js b/chapters/40_json_parse.js new file mode 100644 index 0000000..19569b4 --- /dev/null +++ b/chapters/40_json_parse.js @@ -0,0 +1,57 @@ +function jsonParse() { + /** + * ======================================================== + * Basic Syntax and Usage of JSON.parse() + * ======================================================== + * The JSON.parse() method converts a JSON-formatted string into a JavaScript object or value. + * + * Syntax: JSON.parse(text, reviver?) + * - text: The JSON string to parse. + * - reviver: A function to transform the resulting object. + */ + const jsonString = '{"name": "Ishtmeet", "age": 25}'; + const parsedObject = JSON.parse(jsonString); + console.log(`Basic Usage:`, parsedObject); // Output: { name: 'Ishtmeet', age: 25 } + + /** + * ======================================================== + * Using Reviver Function + * ======================================================== + * The reviver function is used for post-processing the result. + * It receives two arguments: 'key' and 'value'. + */ + const reviverFunction = (key, value) => { + if (key === "age") { + return value + 1; + } + return value; + }; + const parsedWithReviver = JSON.parse(jsonString, reviverFunction); + console.log(`With Reviver Function:`, parsedWithReviver); // Output: { name: 'Ishtmeet', age: 26 } + + /** + * ======================================================== + * Nuances and Edge Cases + * ======================================================== + */ + + /** + * Malformed JSON Strings + * ---------------------- + * If JSON.parse() encounters a malformed JSON string, it throws a SyntaxError. + */ + // Uncomment the next line to see the error + // const malformedJSON = JSON.parse("{'name': 'Ishtmeet'}"); // Single quotes are not valid + + /** + * Parsing Dates + * ------------- + * JSON.parse() doesn't automatically convert date strings into Date objects. + * You need to manually convert them. + */ + const parsedDateObject = JSON.parse('{"date": "2022-01-01T12:00:00Z"}'); + parsedDateObject.date = new Date(parsedDateObject.date); + console.log(`Parsing Dates:`, parsedDateObject.date instanceof Date); // Output: true +} + +jsonParse(); diff --git a/chapters/41_map.js b/chapters/41_map.js new file mode 100644 index 0000000..2fede5b --- /dev/null +++ b/chapters/41_map.js @@ -0,0 +1,106 @@ +function map() { + /** + * ======================================================== + * Initializing a Map + * ======================================================== + * The Map object is initialized using the new Map() constructor. + * You can optionally pass an array of key-value pairs to initialize the map. + */ + const myMap = new Map([ + ["key1", "value1"], + ["key2", "value2"], + [1, "one"], + ]); + + /** + * ======================================================== + * Adding Elements + * ======================================================== + * The set() method is used to add key-value pairs to the map. + * It replaces the value if the key already exists. + */ + myMap.set("key3", "value3"); + myMap.set({}, "emptyObject"); // Note: Object keys are supported + + /** + * ======================================================== + * Accessing Values + * ======================================================== + * The get() method is used to retrieve the value corresponding to a given key. + */ + console.log(`Accessing Values:`, myMap.get("key1")); // Output: 'value1' + console.log(`Accessing Values:`, myMap.get(1)); // Output: 'one' + + /** + * ======================================================== + * Removing Elements + * ======================================================== + * The delete() method removes a key-value pair from the map. + * It returns true if the item exists and has been removed, otherwise false. + */ + myMap.delete("key1"); + + /** + * ======================================================== + * Checking for Existence + * ======================================================== + * The has() method checks if a key exists in the map. + */ + console.log(`Checking Existence:`, myMap.has("key2")); // Output: true + + /** + * ======================================================== + * Iterating Over a Map + * ======================================================== + * The Map object can be iterated using forEach(), for...of, and its built-in iterators. + */ + myMap.forEach((value, key) => { + console.log(`Iterating using forEach: Key: ${key}, Value: ${value}`); + }); + + /** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + + /** + * Order of Elements + * ----------------- + * Maps maintain the insertion order, unlike regular JavaScript objects. + */ + + /** + * Key Equality + * ------------ + * Maps use the "SameValueZero" equality algorithm. For example, NaN is considered equal to NaN. + */ + myMap.set(NaN, "notANumber"); + console.log(`Key Equality:`, myMap.get(NaN)); // Output: 'notANumber' + + /** + * Chaining + * -------- + * Methods like set() return the Map object itself, allowing for chained calls. + */ + myMap.set("key4", "value4").set("key5", "value5"); + + /** + * Built-in Iterators + * ------------------ + * Maps have built-in iterators and can be looped using for...of. + */ + for (const [key, value] of myMap) { + console.log(`Iterating using for...of: Key: ${key}, Value: ${value}`); + } + + /** + * Converting to Array + * ------------------- + * Maps can be converted to arrays for easier manipulation and traversal. + */ + const mapArray = Array.from(myMap); + console.log(`Converting to Array:`, mapArray); // Output: Array of key-value pairs +} + +map(); diff --git a/chapters/42_weak_map.js b/chapters/42_weak_map.js new file mode 100644 index 0000000..09ea84d --- /dev/null +++ b/chapters/42_weak_map.js @@ -0,0 +1,102 @@ +/** + * ======================================================== + * WeakMap + * ======================================================== + * A WeakMap is a collection of key-value pairs where the keys must be objects + * and the values can be arbitrary values. WeakMaps hold "weak" references to the keys, + * which means that they do not prevent garbage collection in case there are no other references to the object. + */ + +/** + * ======================================================== + * Initializing a WeakMap + * ======================================================== + * WeakMaps are initialized using the `new WeakMap()` constructor. + * You can also initialize it with an iterable of key-value pairs, where keys must be objects. + */ +const myWeakMap = new WeakMap([ + [{}, "firstValue"], + [{}, "secondValue"], +]); + +/** + * ======================================================== + * Adding Elements + * ======================================================== + * The set() method adds a new key-value pair into the WeakMap. + * Remember, the key must be an object. + */ +const obj1 = {}; +const obj2 = {}; + +myWeakMap.set(obj1, "obj1Value"); +myWeakMap.set(obj2, "obj2Value"); + +/** + * ======================================================== + * Accessing Values + * ======================================================== + * The get() method retrieves the value associated with a given object key. + */ +console.log(myWeakMap.get(obj1)); // Output: 'obj1Value' + +/** + * ======================================================== + * Removing Elements + * ======================================================== + * The delete() method removes the key-value pair associated with a given object key. + */ +myWeakMap.delete(obj1); // Will remove obj1 and its associated value + +/** + * ======================================================== + * Checking for Existence + * ======================================================== + * The has() method checks if a WeakMap contains a specific key. + */ +console.log(myWeakMap.has(obj2)); // Output: true + +/** + * ======================================================== + * WeakMap Nuances and Limitations + * ======================================================== + */ + +/** + * No Iteration Support + * -------------------- + * WeakMaps can't be iterated using methods like `forEach` or for...of loops. + * This is because they are designed to be 'garbage-collection friendly' and do not have enumerable keys. + */ + +/** + * Garbage Collection + * ------------------ + * WeakMaps don't prevent the keys from being garbage collected. + * If there are no other references to the object, the garbage collector will remove it and its associated value from the WeakMap. + */ + +/** + * No Primitive Data Types as Keys + * ------------------------------- + * Primitive data types (numbers, strings, etc.) cannot be used as keys. + */ + +/** + * No `size` Property + * ------------------ + * Unlike Maps, WeakMaps do not have a `size` property. + */ + +/** + * No Clear Method + * --------------- + * WeakMaps do not have a `clear()` method to remove all key-value pairs. + */ + +/** + * Chainable Methods + * ----------------- + * Methods like set() return the WeakMap object itself, allowing for chained calls. + */ +myWeakMap.set(obj2, "newValue").set(obj1, "obj1NewValue"); diff --git a/chapters/43_set.js b/chapters/43_set.js new file mode 100644 index 0000000..5ed3f7f --- /dev/null +++ b/chapters/43_set.js @@ -0,0 +1,117 @@ +/** + * ======================================================== + * Set + * ======================================================== + * A Set is a data structure that allows you to store unique values of any type, + * whether they are primitive types or objects. Sets are particularly useful when you + * want to avoid duplicate values. + */ + +/** + * ======================================================== + * 1. Initializing a Set + * ======================================================== + * You can initialize a set with the `new Set()` constructor. + * You can also pass an iterable object (like an array) to the constructor to initialize the set with values. + */ +const mySet = new Set([1, 2, 3, 4, 5]); + +/** + * ======================================================== + * 2. Adding Elements + * ======================================================== + * The add() method is used to insert a new element into the set. + */ +mySet.add(6); +mySet.add("six"); + +/** + * ======================================================== + * 3. Removing Elements + * ======================================================== + * The delete() method removes a specific element from the set. + * It returns true if the element is found and removed, otherwise it returns false. + */ +const wasDeleted = mySet.delete(1); // Returns true because '1' was in the set and has been removed + +/** + * ======================================================== + * 4. Checking for Existence + * ======================================================== + * The has() method returns a boolean indicating whether an element is present in the set. + */ +console.log(mySet.has(2)); // Output will be true + +/** + * ======================================================== + * 5. Clearing All Elements + * ======================================================== + * The clear() method is used to remove all elements from the set. + */ +// Uncomment the following line to clear the set +// mySet.clear(); + +/** + * ======================================================== + * 6. Iterating Over a Set + * ======================================================== + * The forEach method and for...of loop can be used to iterate over the Set. + */ +mySet.forEach((value) => { + console.log(value); // Logs each value in the set +}); + +// Using for...of +for (const value of mySet) { + console.log(value); +} + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * Uniqueness is Strict + * -------------------- + * Sets enforce strict uniqueness; even two objects with the same shape are considered different. + */ +mySet.add({}); +mySet.add({}); // Both objects will be added since they are different references + +/** + * Primitive Uniqueness + * --------------------- + * In a set, NaN is considered equal to NaN, which is different from the Array behavior. + */ +mySet.add(NaN); +mySet.add(NaN); // Won't add another NaN as it is already present + +/** + * Order of Elements + * ----------------- + * Sets maintain the insertion order, which means items will be iterated in the order in which they were added. + */ + +/** + * Initialization Shortcuts + * ------------------------ + * Sets can be initialized from arrays or even other sets, effectively removing any duplicates from arrays. + */ +const arraySet = new Set([1, 2, 2, 3, 4]); // Will remove the duplicate '2' +const anotherSet = new Set(mySet); // Creates a new set from an existing one + +/** + * Set Size + * -------- + * You can check the size (number of unique elements) of the set using the size property. + */ +console.log(mySet.size); // Outputs the number of unique elements + +/** + * Chaining Methods + * ---------------- + * Since add() returns the Set object, you can chain multiple add() calls together. + */ +mySet.add(7).add(8); diff --git a/chapters/44_weak_map.js b/chapters/44_weak_map.js new file mode 100644 index 0000000..eacbb50 --- /dev/null +++ b/chapters/44_weak_map.js @@ -0,0 +1,91 @@ +/** + * ======================================================== + * WeakSet in JavaScript + * ======================================================== + * A WeakSet is a special kind of set that holds only objects (not primitive types) + * and does not prevent them from being garbage-collected. This makes it particularly + * useful for scenarios that require temporary storage of object references. + */ + +/** + * ======================================================== + * 1. Initializing a WeakSet + * ======================================================== + * You can initialize a WeakSet using the `new WeakSet()` constructor. + * The constructor optionally accepts an iterable of objects. + */ +const weakSet = new WeakSet([{ a: 1 }, { b: 2 }]); + +/** + * ======================================================== + * 2. Adding Elements + * ======================================================== + * To add an object to a WeakSet, use the add() method. + * Note that only objects can be added, not primitive values. + */ +const myObj = { c: 3 }; +weakSet.add(myObj); + +/** + * ======================================================== + * 3. Removing Elements + * ======================================================== + * The delete() method removes a specific object from the WeakSet. + * It returns true if the object is found and successfully removed. + */ +const wasDeleted = weakSet.delete(myObj); // Returns true as 'myObj' was in the set and has been removed + +/** + * ======================================================== + * 4. Checking for Existence + * ======================================================== + * The has() method returns a boolean indicating whether an object exists in the WeakSet. + */ +console.log(weakSet.has(myObj)); // Output will be false because it's been removed + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * Garbage Collection + * ------------------ + * Since WeakSets hold weak references to objects, the JavaScript engine can safely garbage-collect them. + * This is useful when you want a collection of objects that does not prevent those objects from being garbage-collected. + */ + +/** + * Non-Enumerable + * -------------- + * Unlike Sets, WeakSets are non-enumerable, which means you cannot iterate over them. + * This lack of enumerability is by design to allow the JavaScript engine to perform garbage collection. + */ + +/** + * Limited API + * ----------- + * WeakSets do not have methods like size, keys, values, or forEach. They also lack a clear() method. + * This is intentional, again for allowing better garbage collection. + */ + +/** + * No Primitive Values + * ------------------- + * WeakSets can only store objects. Adding primitive values like numbers or strings will throw a TypeError. + */ + +/** + * Use-Cases + * --------- + * WeakSets are commonly used for scenarios where you want a collection that won't prevent its items from being garbage-collected. + * This makes it useful for things like storing DOM elements that may be removed from the DOM at any time. + */ + +/** + * No Duplicates + * ------------- + * Just like Sets, WeakSets also enforce uniqueness among their elements. + * Adding the same object more than once will have no effect. + */ diff --git a/chapters/45_generators.js b/chapters/45_generators.js new file mode 100644 index 0000000..052dd69 --- /dev/null +++ b/chapters/45_generators.js @@ -0,0 +1,120 @@ +/** + * ======================================================== + * JavaScript Generators + * ======================================================== + * Generators are special functions that allow you to pause and resume their execution, + * preserving their internal state between pauses. This is particularly useful for things + * like iteration, asynchronous operations, and more. + * + * Generators offer a unique way to handle iterative or asynchronous logic + * in a cleaner, more manageable manner. + */ + +/** + * ======================================================== + * Defining a Generator Function + * ======================================================== + * A generator function is defined using the 'function*' syntax. + * When this function is called, it returns a generator object. + */ +function* myGenerator() { + yield "apple"; + yield "banana"; + return "done"; +} + +/** + * ======================================================== + * Creating a Generator Object + * ======================================================== + * You must first call the generator function to create a generator object. + * This object adheres to both the iterable and iterator protocols. + */ +const gen = myGenerator(); // Returns a generator object + +/** + * ======================================================== + * Iterating through a Generator + * ======================================================== + * Use the next() method on the generator object to iterate through the generator function's yields. + * Each call to next() returns an object with 'value' and 'done' properties. + */ +console.log(gen.next()); // Output: { value: 'apple', done: false } +console.log(gen.next()); // Output: { value: 'banana', done: false } +console.log(gen.next()); // Output: { value: 'done', done: true } + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * Generator as an Iterable + * ------------------------ + * A generator object is also an iterable, which means it can be used in a 'for...of' loop. + */ +for (const fruit of myGenerator()) { + console.log(fruit); // Output: 'apple', 'banana' +} + +/** + * yield* Expression + * ---------------- + * The 'yield*' expression can be used to delegate to another generator function or iterable object. + */ +function* anotherGenerator() { + yield* [1, 2, 3]; + yield* myGenerator(); +} +for (const val of anotherGenerator()) { + console.log(val); // Output: 1, 2, 3, 'apple', 'banana' +} + +/** + * Passing Values into Generators + * ------------------------------ + * You can pass values back into a paused generator function using the next(value) method. + */ +function* counter() { + let count = 0; + while (true) { + const increment = yield count; + count += increment || 1; + } +} +const cnt = counter(); +console.log(cnt.next().value); // Output: 0 +console.log(cnt.next(2).value); // Output: 2 + +/** + * Error Handling + * -------------- + * You can handle errors within a generator function using try-catch blocks. + */ +function* failSafeGenerator() { + try { + yield "OK"; + throw new Error("An error occurred"); + } catch (error) { + yield "Recovered from error"; + } +} +const safeGen = failSafeGenerator(); +console.log(safeGen.next().value); // Output: 'OK' +console.log(safeGen.next().value); // Output: 'Recovered from error' + +/** + * Asynchronous Generators + * ------------------------ + * ES2018 introduced asynchronous generator functions, defined using 'async function*'. + * They yield Promises and can be consumed using 'for await...of' loops. + */ +async function* asyncGenerator() { + yield Promise.resolve("async value"); +} +(async () => { + for await (const val of asyncGenerator()) { + console.log(val); // Output: 'async value' + } +})(); diff --git a/chapters/46_iterators.js b/chapters/46_iterators.js new file mode 100644 index 0000000..2b2357a --- /dev/null +++ b/chapters/46_iterators.js @@ -0,0 +1,129 @@ +/** + * ======================================================== + * JavaScript Iterators + * ======================================================== + * Iterators are a mechanism to traverse through collections (go through each item in a collection one by one), + * such as arrays, strings, and more advanced data structures that we just discussed - like maps and sets. + */ + +/** + * ======================================================== + * The Iterator Protocol + * ======================================================== + * The iterator protocol in JavaScript requires that you implement a 'next()' method. + * This method must return an object with 'value' and 'done' properties. + * + * The next() method serves as the core of the Iterator Protocol. It should fulfill the following conditions: + * + * -> Return an Object: It must return an object. + * + * -> Object Properties: The returned object should have two properties: + * 1. value: Holds the value of the current iteration. + * 2. done: A Boolean value that is true if the iterator is past the end of the iterated sequence. + * + * In JavaScript, it is conventional to return {value: undefined, done: true} when the sequence is exhausted. + */ +const arrayIterator = ["a", "b", "c"][Symbol.iterator](); + +console.log(arrayIterator.next()); // Output: { value: 'a', done: false } +console.log(arrayIterator.next()); // Output: { value: 'b', done: false } +console.log(arrayIterator.next()); // Output: { value: 'c', done: false } +console.log(arrayIterator.next()); // Output: { value: undefined, done: true } + +/** + * ======================================================== + * Custom Iterators + * ======================================================== + * You can make your own iterable objects by defining a [Symbol.iterator] method. + * This method should return an object containing a 'next' method. + */ +const customIterable = { + [Symbol.iterator]: function () { + let count = 0; + return { + next: function () { + count++; + if (count <= 3) { + return { value: count, done: false }; + } + return { value: undefined, done: true }; + }, + }; + }, +}; + +// Using the custom iterable in a for...of loop +for (const item of customIterable) { + console.log(item); // Output: 1, 2, 3 +} + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * Iterating Over Arguments + * ------------------------ + * JavaScript's 'arguments' object is array-like but not an actual array. + * You can make it iterable using Array.from() method. + */ +function argumentsIterator() { + const argsArray = Array.from(arguments); + for (const arg of argsArray) { + console.log(arg); + } +} +argumentsIterator(1, 2, 3); // Output: 1, 2, 3 + +/** + * String Iterators + * ---------------- + * Strings are iterable by default. When you iterate over a string, + * each iteration returns a single character. + */ +for (const char of "hello") { + console.log(char); // Output: 'h', 'e', 'l', 'l', 'o' +} + +/** + * Iterating Over Maps and Sets + * ---------------------------- + * Map and Set objects have their built-in iterators, which makes it easier to iterate through them. + */ +const mySet = new Set([1, 2, 3]); +const setIter = mySet[Symbol.iterator](); +console.log(setIter.next().value); // Output: 1 + +const myMap = new Map([ + ["a", 1], + ["b", 2], +]); +const mapIter = myMap[Symbol.iterator](); +console.log(mapIter.next().value); // Output: ['a', 1] + +/** + * Iterator Return and Throw Methods + * --------------------------------- + * Besides the 'next()' method, iterators can optionally implement 'return' and 'throw' methods. + * These can be used to release internal resources when an iterator is no longer in use, + * or to propagate errors during iteration, respectively. + */ +const advancedIterator = { + [Symbol.iterator]: function () { + return { + next() { + return { value: "some_value", done: false }; + }, + return() { + console.log("Exiting early"); + return { done: true }; + }, + throw(error) { + console.log("An error occurred:", error); + return { done: true }; + }, + }; + }, +}; diff --git a/chapters/47_big_int.js b/chapters/47_big_int.js new file mode 100644 index 0000000..e1fef58 --- /dev/null +++ b/chapters/47_big_int.js @@ -0,0 +1,93 @@ +/** + * ======================================================== + * BigInt + * ======================================================== + * BigInt is a built-in object that provides a way to represent whole numbers larger + * than 2^53 - 1, which is the largest number JavaScript can reliably represent using the Number primitive. + */ + +/** + * ======================================================== + * 1. Basic Syntax + * ======================================================== + * BigInt can be defined either by appending 'n' to the end of an integer literal or + * by using the BigInt constructor function. + */ +const bigIntLiteral = 1234567890123456789012345678901234567890n; +const bigIntObject = BigInt("1234567890123456789012345678901234567890"); + +/** + * ======================================================== + * 2. Arithmetic Operations + * ======================================================== + * Arithmetic operations are supported on BigInts much like they are for Numbers, + * but you can't mix BigInts and Numbers in these operations. + */ +const sum = bigIntLiteral + 1n; +// const invalidSum = bigIntLiteral + 1; // This will throw a TypeError + +/** + * ======================================================== + * 3. Comparison + * ======================================================== + * BigInt can be compared using all the regular comparison operators. + * Just like arithmetic operations, you can't directly compare a BigInt with a Number. + */ +const isTrue = bigIntLiteral > 1000n; // Evaluates to true + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. No Auto-Type Conversion + * --------------------------- + * BigInt does not automatically convert into a string or a number. + * Explicit conversion is often required when you're working with mixed types. + */ +const bigIntStr = String(bigIntLiteral); +// const invalidOperation = bigIntLiteral + " is big."; // This will throw a TypeError + +/** + * 2. Divisions Are Floored + * ------------------------ + * When performing division using BigInt, the division is floored, + * meaning it rounds towards zero. + */ +const flooredDivision = 11n / 3n; // Result will be 3n + +/** + * 3. Bitwise Operations + * --------------------- + * You can perform bitwise operations with BigInt. + * Both operands must be BigInts to perform these operations. + */ +const bitwiseAnd = 16n & 5n; // Output will be 0n + +/** + * 4. No Support for Math Object + * ----------------------------- + * BigInt cannot be used with the JavaScript Math object's methods. + */ +// const sqrtBigInt = Math.sqrt(bigIntLiteral); // This will throw a TypeError + +/** + * 5. JSON Serialization + * ---------------------- + * JSON.stringify() can't serialize BigInt values, + * you'll need to manually convert them to strings before serialization. + */ +// const bigIntJSON = JSON.stringify({value: bigIntLiteral}); // This will throw a TypeError + +/** + * 6. Compatibility + * ---------------- + * BigInt is not yet universally supported across all environments, + * so you may need to check for compatibility before using it. + */ +if (typeof BigInt !== "undefined") { + const bigIntSupport = BigInt(10); + console.log(`BigInt supported: ${bigIntSupport}`); +} diff --git a/chapters/48.0_web_apis.js b/chapters/48.0_web_apis.js new file mode 100644 index 0000000..bf487b1 --- /dev/null +++ b/chapters/48.0_web_apis.js @@ -0,0 +1,138 @@ +/** + * ======================================================== + * Web APIs - Part 1 + * ======================================================== + * Web APIs augment the capabilities of JavaScript, allowing developers to interact + * with the web browser or other hosting environments. Web APIs often provide a way + * to perform operations that aren't possible with JavaScript alone. + */ + +/** + * ======================================================== + * 1. XMLHttpRequest API + * ======================================================== + * The traditional method for making HTTP requests in JavaScript. + * While it's mostly superseded by the Fetch API, it's still widely used in legacy codebases. + */ +const xhr = new XMLHttpRequest(); +xhr.open("GET", "https://api.example.com/data", true); +xhr.send(); +xhr.onload = () => { + if (xhr.status === 200) { + console.log(xhr.response); + } else { + console.log(`Error: ${xhr.status}`); + } +}; + +/** + * ======================================================== + * 2. Fetch API + * ======================================================== + * A modern way to fetch resources over the network. It returns Promises and + * is much cleaner and more powerful than XMLHttpRequest. + */ +fetch("https://api.example.com/data") + .then((response) => { + if (!response.ok) { + throw new Error("Network response was not ok"); + } + return response.json(); + }) + .then((data) => console.log(data)) + .catch((error) => console.error("Fetch Error:", error)); + +/** + * ======================================================== + * 3. Geolocation API + * ======================================================== + * This API is used to get the geographic position of a user. Note that it requires user consent. + */ +navigator.geolocation.getCurrentPosition( + (position) => { + const { latitude, longitude } = position.coords; + console.log(`Latitude: ${latitude}, Longitude: ${longitude}`); + }, + (error) => console.error("Geolocation Error:", error) +); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. AbortController for Fetch API + * -------------------------------- + * The AbortController is used to terminate fetch requests. + * This can be helpful to abort requests based on certain conditions, like a timeout. + */ +const controller = new AbortController(); +const { signal } = controller; +fetch("https://api.example.com/data", { signal }) + .then((response) => response.json()) + .catch((err) => { + if (err.name === "AbortError") { + console.log("Fetch aborted"); + } + }); +// To abort the fetch +controller.abort(); + +/** + * 2. Using the Cache API + * ---------------------- + * This API allows you to cache network resources. This can significantly improve + * the performance of web applications by reducing load times. + */ +caches.open("my-cache").then((cache) => { + cache + .add("https://api.example.com/data") + .then(() => console.log("Data cached")) + .catch((error) => console.error("Cache Error:", error)); +}); + +/** + * 3. Clipboard API + * ---------------- + * The Clipboard API is used for interacting with the clipboard to read and write text. + */ +navigator.clipboard + .writeText("Copied text") + .then(() => console.log("Text copied to clipboard")) + .catch((err) => console.error("Clipboard Error:", err)); + +/** + * 4. Notification API + * ------------------- + * Allows you to display native system notifications. + * This can enhance the user experience by providing real-time updates. + */ +if (Notification.permission === "granted") { + new Notification("Hello, world!"); +} else { + Notification.requestPermission().then((permission) => { + if (permission === "granted") { + new Notification("Hello, world!"); + } + }); +} + +/** + * 5. Intersection Observer API + * ---------------------------- + * Useful for detecting when an element enters or exits the viewport. + * Common use-cases include lazy-loading images and infinite scrolling. + */ +const observer = new IntersectionObserver((entries) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + // Perform action, such as lazy-loading an image + console.log("Element is in the viewport!"); + } + }); +}); + +// To observe a specific element +observer.observe(document.querySelector(".some-element")); diff --git a/chapters/48.1_web_apis_2.js b/chapters/48.1_web_apis_2.js new file mode 100644 index 0000000..62b8390 --- /dev/null +++ b/chapters/48.1_web_apis_2.js @@ -0,0 +1,103 @@ +/** + * ======================================================== + * Web APIs - Part 2 + * ======================================================== + */ + +/** + * ======================================================== + * 1. WebSocket API + * ======================================================== + * WebSocket provides full-duplex communication channels over a single, long-lived connection. + */ +const ws = new WebSocket("ws://example.com/socketserver"); +ws.onopen = () => { + ws.send("Hello Server!"); +}; +ws.onmessage = (event) => { + console.log(`Server says: ${event.data}`); +}; +ws.onclose = () => { + console.log("Connection closed"); +}; + +/** + * ======================================================== + * 2. DOM API + * ======================================================== + * The DOM API allows you to programmatically interact with HTML and XML documents. + */ +// Adding a new element +const newElement = document.createElement("div"); +newElement.textContent = "Hello, World!"; +document.body.appendChild(newElement); + +/** + * ======================================================== + * 3. Drag and Drop API + * ======================================================== + * This API allows you to make elements draggable and to capture drop events. + */ +document.addEventListener("dragstart", (event) => { + event.dataTransfer.setData("text/plain", event.target.id); +}); +document.addEventListener("drop", (event) => { + event.preventDefault(); + const data = event.dataTransfer.getData("text"); + const target = document.getElementById(data); + event.target.appendChild(target); +}); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. WebSocket Reconnection + * -------------------------- + * In real-world scenarios, WebSocket connections can drop. A common practice is to implement + * reconnection logic. + */ +let ws; +function connect() { + ws = new WebSocket("ws://example.com/socketserver"); + ws.onopen = () => { + ws.send("Hello Server!"); + }; + ws.onmessage = (event) => { + console.log(`Server says: ${event.data}`); + }; + ws.onclose = () => { + console.log("Connection closed. Reconnecting..."); + setTimeout(connect, 1000); + }; +} +connect(); + +/** + * 2. Using DOM API with Fragments + * -------------------------------- + * Document fragments let you create a subtree of elements and insert them into the DOM + * as a single operation. + */ +const fragment = document.createDocumentFragment(); +const elem1 = document.createElement("p"); +elem1.textContent = "Paragraph 1"; +const elem2 = document.createElement("p"); +elem2.textContent = "Paragraph 2"; +fragment.appendChild(elem1); +fragment.appendChild(elem2); +document.body.appendChild(fragment); + +/** + * 3. Drag and Drop with Custom Images + * ----------------------------------- + * You can customize the drag image by setting the `dragImage` property on the dataTransfer object. + */ +document.addEventListener("dragstart", (event) => { + const img = new Image(); + img.src = "path/to/image.png"; + event.dataTransfer.setDragImage(img, 10, 10); +}); diff --git a/chapters/48.2_web_apis_3.js b/chapters/48.2_web_apis_3.js new file mode 100644 index 0000000..755c145 --- /dev/null +++ b/chapters/48.2_web_apis_3.js @@ -0,0 +1,79 @@ +/** + * ======================================================== + * Web APIs - Part 3 + * ======================================================== + */ + +/** + * ======================================================== + * 1. Audio and Video API + * ======================================================== + * The HTML5 Audio and Video APIs allow for playback of multimedia. + */ +const audio = new Audio("audio_file.mp3"); +audio.play(); + +const video = document.querySelector("video"); +video.play(); + +/** + * ======================================================== + * 2. Canvas API + * ======================================================== + * The Canvas API provides a way to draw 2D graphics. + */ +const canvas = document.getElementById("myCanvas"); +const ctx = canvas.getContext("2d"); +ctx.fillStyle = "#FF0000"; +ctx.fillRect(0, 0, 80, 80); + +/** + * ======================================================== + * 3. RequestAnimationFrame API + * ======================================================== + * Used for creating smooth animations. + */ +function animate() { + // Animation code + requestAnimationFrame(animate); +} +animate(); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. Controlling Audio and Video Playback + * --------------------------------------- + * You can control the playback rates, volume, and other properties. + */ +audio.volume = 0.5; +audio.playbackRate = 1.5; + +/** + * 2. Canvas Transformations + * ------------------------- + * Canvas allows you to perform complex transformations like scaling and rotations. + * We'll talk more about canvas, in the next chapter. + */ +ctx.save(); +ctx.rotate((Math.PI / 180) * 45); +ctx.fillRect(100, 0, 50, 50); +ctx.restore(); + +/** + * 3. Animating with RequestAnimationFrame + * --------------------------------------- + * It's best to use requestAnimationFrame over setInterval for smoother animations. + */ +let x = 0; +function animate() { + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(x, 0, 50, 50); + x += 1; + requestAnimationFrame(animate); +} +animate(); diff --git a/chapters/49_canvas.js b/chapters/49_canvas.js new file mode 100644 index 0000000..2b70b9e --- /dev/null +++ b/chapters/49_canvas.js @@ -0,0 +1,126 @@ +/** + * ======================================================== + * Canvas API + * ======================================================== + * The Canvas API allows for dynamic, scriptable rendering of 2D and 3D graphics. + */ + +/** + * ======================================================== + * 1. Basic Setup + * ======================================================== + * Create a canvas element in HTML and get its context in JavaScript. + */ +const canvas = document.getElementById("myCanvas"); +const ctx = canvas.getContext("2d"); + +/** + * ======================================================== + * 2. Drawing Shapes + * ======================================================== + * Draw simple shapes like rectangles, circles, and lines. + */ + +// Draw a rectangle +ctx.fillStyle = "red"; +ctx.fillRect(10, 10, 100, 50); + +// Draw a circle +ctx.fillStyle = "blue"; +ctx.beginPath(); +ctx.arc(100, 100, 50, 0, Math.PI * 2); +ctx.fill(); + +// Draw a line +ctx.strokeStyle = "green"; +ctx.beginPath(); +ctx.moveTo(10, 10); +ctx.lineTo(100, 10); +ctx.stroke(); + +/** + * ======================================================== + * 3. Text Rendering + * ======================================================== + * Display text on the canvas. Can control font, alignment, and more. + */ +ctx.font = "30px Arial"; +ctx.fillText("Hello Canvas", 50, 200); +ctx.textAlign = "center"; // Other options: 'left', 'right' +ctx.fillText("Centered Text", canvas.width / 2, canvas.height / 2); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. Pixel Manipulation + * ---------------------- + * Manipulate individual pixels for advanced effects like filters. + */ +const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); +const data = imageData.data; +for (let i = 0; i < data.length; i += 4) { + // Invert colors + data[i] = 255 - data[i]; + data[i + 1] = 255 - data[i + 1]; + data[i + 2] = 255 - data[i + 2]; +} +ctx.putImageData(imageData, 0, 0); + +/** + * 2. Transformations + * ------------------- + * Apply transformations like scale, rotate, and translate. + * Always save and restore the context when doing transformations. + */ +ctx.save(); +ctx.scale(0.5, 0.5); +ctx.rotate((Math.PI / 180) * 25); +ctx.translate(100, 0); +// Draw transformed rectangle +ctx.fillStyle = "purple"; +ctx.fillRect(50, 50, 100, 50); +ctx.restore(); + +/** + * 3. Animation + * ------------- + * Use `requestAnimationFrame` for smooth animations. + * This creates a game loop for continuous drawing. + */ +let xPos = 0; +function drawFrame() { + // Clear canvas and update drawings + ctx.clearRect(0, 0, canvas.width, canvas.height); + ctx.fillRect(xPos, 10, 50, 50); + xPos++; + requestAnimationFrame(drawFrame); +} +requestAnimationFrame(drawFrame); + +/** + * 4. OffscreenCanvas + * ------------------- + * Perform canvas rendering in a web worker to offload the main thread. + */ +if (window.OffscreenCanvas) { + const offscreen = new OffscreenCanvas(100, 100); + const offscreenCtx = offscreen.getContext("2d"); + offscreenCtx.fillStyle = "orange"; + offscreenCtx.fillRect(0, 0, 100, 100); +} + +/** + * 5. Path2D Object + * ---------------- + * Use Path2D objects to cache complex paths and draw them later. + * This can improve performance in animations. + */ +const path = new Path2D(); +path.moveTo(10, 10); +path.lineTo(100, 10); +path.lineTo(100, 100); +ctx.stroke(path); diff --git a/chapters/50_drag_drop.js b/chapters/50_drag_drop.js new file mode 100644 index 0000000..69b329c --- /dev/null +++ b/chapters/50_drag_drop.js @@ -0,0 +1,126 @@ +/** + * ======================================================== + * JavaScript Drag and Drop API + * ======================================================== + * Enables interactive Drag-and-Drop interfaces in web applications. + */ + +/** + * ======================================================== + * 1. Making an Element Draggable + * ======================================================== + * To make an element draggable, set its 'draggable' attribute to true. + * This enables the browser's native drag-and-drop feature for the element. + */ +const draggableElem = document.getElementById("draggable"); +draggableElem.setAttribute("draggable", "true"); + +/** + * ======================================================== + * 2. Drag Events + * ======================================================== + * Drag events are fired on both the draggable target and the drop target. + * The 'dragstart' event is essential for initiating a drag operation. + */ +// Drag start event +draggableElem.addEventListener("dragstart", (event) => { + // Store some meta-data to be used during the 'drop' event + event.dataTransfer.setData("text/plain", draggableElem.id); +}); + +/** + * ======================================================== + * 3. Dropping an Element + * ======================================================== + * To allow an element to act as a drop target, you must prevent the default handling of the element during dragover. + */ +const dropZone = document.getElementById("dropZone"); + +// Allow the drop by preventing default behavior +dropZone.addEventListener("dragover", (event) => { + event.preventDefault(); +}); + +// Handle the drop +dropZone.addEventListener("drop", (event) => { + event.preventDefault(); + const data = event.dataTransfer.getData("text/plain"); + const draggedElem = document.getElementById(data); + dropZone.appendChild(draggedElem); +}); + +/** + * ======================================================== + * Nuances and Advanced Techniques + * ======================================================== + */ + +/** + * 1. Custom Drag Image + * -------------------- + * You can set a custom image to appear next to the cursor while dragging. + */ +draggableElem.addEventListener("dragstart", (event) => { + const img = new Image(); + img.src = "drag-image.png"; + event.dataTransfer.setDragImage(img, 10, 10); +}); + +/** + * 2. Drag Handles + * ---------------- + * You can make only a part of an element act as a drag handle. + */ +const handle = document.getElementById("handle"); +handle.addEventListener("dragstart", (event) => { + event.dataTransfer.setData("text/plain", draggableElem.id); + event.stopPropagation(); // Prevent dragstart from bubbling up to parent elements +}); + +/** + * 3. Nested Drop Targets + * ---------------------- + * Handling dragover and drop events properly when there are nested drop targets can be tricky. + * Stop propagation to ensure only the innermost target gets the event. + */ +const nestedZone = document.getElementById("nestedZone"); +nestedZone.addEventListener("dragover", (event) => { + event.stopPropagation(); + event.preventDefault(); // Still required to allow drop +}); +nestedZone.addEventListener("drop", (event) => { + event.stopPropagation(); + event.preventDefault(); // Execute your nested drop logic here +}); + +/** + * 4. Drag and Drop File Upload + * ---------------------------- + * You can use drag and drop for file uploads. It enhances user experience significantly. + */ +dropZone.addEventListener("drop", (event) => { + const files = event.dataTransfer.files; + Array.from(files).forEach((file) => { + // Your file processing logic here + console.log(`Uploaded file: ${file.name}`); + }); +}); + +/** + * HTML Code for testing the above - + + + +
+