JavaScript Interview Prep: Key Concepts
JavaScript Interview Prep: Key Concepts
JavaScript Promises and async/await provide a more structured and readable means of handling asynchronous operations than traditional callbacks by flattening the asynchronous code's nested structure ('callback hell'). Promises allow results to be handled through a chain of '.then()', '.catch()', and '.finally()' calls, delineating resolved values, errors, and cleanup processes. Async/await syntax further simplifies asynchronous code by making it appear synchronous, allowing developers to handle sequential asynchronous operations more naturally .
The Temporal Dead Zone (TDZ) in JavaScript refers to the time span between the start of the current scope and the point at which a 'let' or 'const' variable is declared. During the TDZ, accessing an uninitialized 'let' or 'const' variable throws a ReferenceError. Although the variable is hoisted to the top of its scope, it remains uninitialized and inaccessible until the code execution hits its declaration, restricting any usage or reference until this initialization point .
'for(var)' uses function scoping, leading to a single shared memory reference across loop iterations in asynchronous operations like setTimeout. This results in the output reflecting the loop completion value, typically 10 repeats of the last value. In contrast, 'for(let)' is block-scoped, creating a new copy of the loop variable 'i' for each iteration. This allows each asynchronous setTimeout to capture and log each block-scoped copy of 'i', producing a sequential output from 0 to 9 .
Arrow functions do not create their own 'this' binding; instead, they lexically inherit 'this' from the surrounding non-arrow function or global scope. This means the 'this' value within an arrow function is determined by the context in which the arrow function was defined, not where it is invoked. This behavior affects execution context by preventing 'this' from being dynamically altered, making arrow functions ideal for callbacks that should preserve the original 'this' context .
Consider the code snippet: console.log(a); var a = 10; function greet() { console.log('Hello'); }. During the Memory Creation Phase, 'a' is hoisted as 'undefined' and 'greet' as a complete function. By the Execution Phase, the first line attempts to log 'a', which is 'undefined' at this point due to hoisting. Following execution, the variable 'a' is then assigned value '10'. Function 'greet' can be safely called at any code point due to its fully hoisted state with body .
Using 'var' in loop variable declarations results in a single shared memory reference with function-scoping during asynchronous operations. Therefore, by the time callbacks execute, 'var' captures the loop's completed state, typically leading to unexpected identical values in outputs like with setTimeout calls. Conversely, 'let' introduces block-scoping, creating a new, independent instance for each iteration, allowing callbacks to log different values corresponding to each loop's iteration derivative, offering more predictable results .
Variables declared with 'var' are hoisted as 'undefined', causing a TypeError if invoked as a function before assignment. 'let' and 'const' declarations are subject to the Temporal Dead Zone (TDZ). Accessing them before initialization leads to a ReferenceError because they aren't initialized prior to usage, effectively creating a state where the variable is inaccessible despite existing in memory .
An IIFE resolves closure issues in loops using 'var' by immediately capturing the current loop variable's state in its own scope each iteration. This ensures each iteration of asynchronous operations like setTimeout has access to the closures capturing the distinct value of 'i' at that time, rather than sharing one mutable reference as would occur without an IIFE. Consequently, each callback logs a unique value corresponding to the loop's current increment .
JavaScript Execution Context operates in two key phases: the Memory Creation Phase and the Execution Phase. In the Memory Creation Phase, variables and function declarations are hoisted. Variables declared with 'var' are initialized as 'undefined', whereas those with 'let' and 'const' enter the Temporal Dead Zone (TDZ) and remain uninitialized until the line of code is executed. Function declarations are hoisted with their complete body. In the Execution Phase, the code is executed line-by-line, with variables being initialized to their actual values and functions invoked as programmed .
Function declarations in JavaScript are fully hoisted with their body, allowing them to be invoked before the line where they are defined. In contrast, function expressions are only partially hoisted as variables: if using 'var', the function is hoisted as 'undefined', leading to a TypeError if invoked before assignment; if using 'let' or 'const', a ReferenceError occurs due to the Temporal Dead Zone (TDZ).