Feed

Log in to post, follow alumni, and join the conversation.

Abdul Rehman avatar

If a function finishes executing, its memory is wiped out. So how does this code remember the count? function createCounter() { let count = 0; return function() { count++; return count; }; } const counter = createCounter(); console.log(counter()); // 1 console.log(counter()); // 2 console.log(counter()); // 3 "createCounter()" executed and popped off the Call Stack on line 10. Yet every time we invoke "counter()", it accesses and increments "count" as if the parent function is still running. Is this a loop? No. This is one of the most powerful features in JavaScript: A Closure. Here is what happens under the hood: 1. What Actually Is a Closure? A closure is simply a function bundled together with references to its surrounding lexical environment. Whenever an inner function uses a variable from its outer parent and is returned or passed elsewhere, it doesn't leave those variables behind—it packs them into an invisible "backpack". 2. Where Is the Variable Stored? (Stack vs Heap) Under normal circumstances: • Local variables live on the Call Stack. Once a function returns, its frame is destroyed. With a Closure: • The V8 engine detects that the returned inner function still needs "count". • Instead of leaving "count" on the stack to be discarded, the engine allocates it into Heap Memory inside a special Closure object. • Even though the Call Stack frame is gone, the reference in Heap memory stays alive. 3. Why Doesn't the Garbage Collector Delete It? JavaScript's Garbage Collector works via "reachability". As long as the variable "counter" holds a reference to that inner function, the inner function holds a reference to the Heap record. Because the link is active, the engine keeps that memory intact. 4. Proof in the Browser DevTools Run this in your console: console.dir(counter); Expand the dropdown ➔ inspect [[Scopes]] ➔ open "Closure (createCounter)". You will literally see: "count: 3" sitting safely in heap storage. This exact mechanism is how private state, factory functions, and React's useState persist data across independent execution cycles without polluting global scope. Have you ever inspected a function's [[Scopes]] property inside DevTools to debug closure leaks?

GHULAM-E-MUSTAFA IMRAN avatar

💡 Learning to Think Beyond Code My web development journey at Saylani is about more than learning to write code. It's also about developing logical thinking, improving problem-solving skills, and learning how to use modern AI tools responsibly. AI can support our learning, but understanding the concepts is what helps us grow. Learn. Think. Build. Improve. 🚀 #Saylani #WebDevelopment #AI #LearningJourney #ProblemSolving

mrbaigdev avatar
New member

mrbaigdev just joined the alumni network

Node.jsExpressTypeScriptJavaScriptNext.jsMongoDB
sehrishayaz25 avatar
New member

sehrishayaz25 just joined the alumni network

Adobe XDUI/UX DesignFigmaPhotoshopIllustratorGraphic Design
irshadfizza03 avatar
New member

irshadfizza03 just joined the alumni network

IllustratorAdobe XDGraphic DesignSketchPhotoshop
tayyabaismail96 avatar
New member

tayyabaismail96 just joined the alumni network

NetlifyHTMLCSSShopifySocial MediaDigital Marketing
Abdul Rehman avatar

Can you trace where JavaScript finds this variable? const appName = "CoreCloud"; function parent() { const user = "Ali"; function child() { const role = "Admin"; console.log(${user} is an ${role} on ${appName}); } child(); } parent(); Output: "Ali is an Admin on CoreCloud" The inner "child()" function accesses variables it never defined. Yet, if you try accessing "role" outside, JavaScript throws an instant ReferenceError. Why does variable access work seamlessly outward, but fails completely inward? Here is what happens under the hood during Scope Resolution: 1. The Lexical Environment Rule In JavaScript, "Lexical" simply refers to where your code is physically written in your file. Whenever a function is declared, the engine gives it a hidden reference to its parent environment. This link creates the "Scope Chain". 2. The Upward-Only Search Engine When the engine executes "child()", it encounters three variables: • Step 1 (role): Found immediately in its own Local Scope. • Step 2 (user): Not in local scope. The engine climbs up to "parent()" and locates it. • Step 3 (appName): Not in "parent()". The engine climbs further up to the Global Scope and finds it. The Golden Rule of the Scope Chain: Lookup only moves from Inside ➔ Outside. An outer function can never peek inside an inner child's scope. 3. Variable Shadowing: The Hidden Mask What happens if two scopes use the exact same variable name? let count = 10; function update() { let count = 50; console.log("Inside:", count); } update(); console.log("Outside:", count); Output: Inside: 50 Outside: 10 The inner variable "shadows" (masks) the outer one. It temporarily hides the outer reference without overwriting or mutating it. 4. The Interview Trap: Illegal Shadowing Shadowing works safely with "let" and "const", but introduces breaking errors with "var": let a = 10; { var a = 20; // Uncaught SyntaxError: Identifier 'a' has already been declared } Why? Because "var" is not block-scoped. It attempts to leak out of the curly braces into the outer scope where "let a" already exists, causing an instant compilation error. Mastering the Scope Chain is the fundamental prerequisite to understanding Closures, Lexical Binding, and state encapsulation. Did you know why illegal shadowing crashes with a SyntaxError?

Abdul Rehman avatar

Can you explain why this line evaluates to true in JavaScript? console.log([] == ![]); // true An empty array is equal to the negation of itself? It looks like a broken language feature, but the JavaScript engine follows strict internal conversion rules. Here is what happens under the hood during Implicit Type Coercion: 1. Strict Equality (===) vs Loose Equality (==) • Strict (===): Compares Type + Value. If types differ, it returns false immediately. No conversions occur. • Loose (==): Compares Value only. If types differ, the engine forces "Implicit Type Coercion" to convert both sides into a common type before comparing. 2. Decoding the Anatomy of [] == ![] Step 1: Operator Precedence The Logical NOT (!) operator executes first. In JavaScript, an array is an object, and all objects are truthy. Negating a truthy value turns it into boolean false: ![] ➔ false Now the check becomes: [] == false Step 2: The Boolean Coercion Rule When comparing anything to a Boolean with ==, the engine converts the Boolean to a Number (false becomes 0): [] == 0 Step 3: The Object-to-Primitive Rule When comparing an Object/Array to a Number, the engine calls .toString() on the object: [].toString() produces "" (an empty string). Now the check becomes: "" == 0 Step 4: The String-to-Number Rule When comparing a String to a Number, the engine converts the String to a Number: Number("") evaluates to 0. Step 5: The Final Comparison 0 == 0 ➔ true! 3. The Special Case: null and undefined null == undefined evaluates to true, but: null == 0 evaluates to false. Why? The specification explicitly dictates that null and undefined loosely equal each other and nothing else. They do not coerce to numbers under ==. 4. The Production Standard: This is why modern teams and ESLint enforce the strict equality rule (eqeqeq). Loose equality (==) introduces silent conversions that hide runtime logic bugs. Always default to strict equality (=== and !==) to keep comparisons explicit and predictable. Did you know why [] == ![] returned true before seeing the step-by-step trace?

Syed Muhammad Taha Hassan Shah avatar

It's feel like everyone here is elon musk 😒.

You're at the end of the preview

Log in to keep reading the feed, follow alumni, and join the conversation.