<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Olamide Ilori</title>
    <description>The latest articles on Forem by Olamide Ilori (@the_shydev).</description>
    <link>https://forem.com/the_shydev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2941098%2Fccac9bc2-fe78-4b3b-9d64-cbb4ab0ad9b5.png</url>
      <title>Forem: Olamide Ilori</title>
      <link>https://forem.com/the_shydev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/the_shydev"/>
    <language>en</language>
    <item>
      <title>JavaScript Scoping Essentials You Should Know: Hoisting, Closures, and Lexical Scope</title>
      <dc:creator>Olamide Ilori</dc:creator>
      <pubDate>Wed, 19 Nov 2025 14:02:12 +0000</pubDate>
      <link>https://forem.com/the_shydev/javascript-scoping-essentials-you-should-know-hoisting-closures-and-lexical-scope-1b62</link>
      <guid>https://forem.com/the_shydev/javascript-scoping-essentials-you-should-know-hoisting-closures-and-lexical-scope-1b62</guid>
      <description>&lt;h2&gt;
  
  
  Introduction: When JavaScript Gets Tricky
&lt;/h2&gt;

&lt;p&gt;Sometimes you stumble upon a tiny snippet of JavaScript and immediately feel like the language is testing your sanity: Take the example below:&lt;/p&gt;

&lt;p&gt;Example 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var i = 1; i &amp;lt;= 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 1000);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What do you think it prints? &lt;code&gt;1 2 3&lt;/code&gt;, &lt;code&gt;3 3 3&lt;/code&gt;, or &lt;code&gt;4 4 4&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;If you guessed &lt;code&gt;1 2 3&lt;/code&gt; or &lt;code&gt;3 3 3&lt;/code&gt;, don’t feel too bad, you’ve just fallen into JavaScript’s classic “gotcha” trap. The actual output is: &lt;code&gt;4 4 4&lt;/code&gt;. You may ask, what is &lt;code&gt;4&lt;/code&gt; business in all of this, how on earth is 4 &amp;lt;= 3, well, welcome to JavaScript my friend.  &lt;/p&gt;

&lt;p&gt;Here’s another one:&lt;/p&gt;

&lt;p&gt;Example 2:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (let i = 1; i &amp;lt;= 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 1000);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What do you think it prints? &lt;code&gt;1 2 3&lt;/code&gt;, &lt;code&gt;3 3 3&lt;/code&gt;, or &lt;code&gt;4 4 4&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;Surprise! It prints: &lt;code&gt;1 2 3&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The difference? It’s all about how you declare your variables (&lt;strong&gt;var&lt;/strong&gt; or &lt;strong&gt;let&lt;/strong&gt;). &lt;strong&gt;var&lt;/strong&gt; behaves like a single shared memory cell, while &lt;strong&gt;let&lt;/strong&gt; creates a fresh copy for each iteration. In other words, JavaScript remembers differently depending on your choice. Sounds too technical already? Don't worry, I would break it all down for you at the end of this article. &lt;/p&gt;

&lt;p&gt;But first, let's talk about&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Scoping in JavaScript: Where Your Variables Live&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The real culprit behind these surprises is scoping. Scoping determines where variables are accessible in your code. JavaScript utilizes &lt;strong&gt;lexical (or static) scoping, as opposed to dynamic scoping&lt;/strong&gt;. What then is lexical scoping? &lt;/p&gt;

&lt;p&gt;Lexical Scoping simply means that a variable's scope is determined by where it is written in the source code, not by where it is called at runtime.&lt;br&gt;
In other words:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;When a function is created, it remembers the scope in which it was defined.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;It does not matter where the function is called; only where it was defined matters.&lt;br&gt;
Let's take an example.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Example 3:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;let x = 1;
function foo() {
   console.log(x);
}

function bar() {
   let x = 2;
   foo();
}
bar(); 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outputs &lt;br&gt;
(Lexical Scoping): 1&lt;br&gt;
(Dynamic Scoping) 2&lt;/p&gt;

&lt;p&gt;Let's break down the lexical scoping concept:&lt;br&gt;
&lt;code&gt;foo()&lt;/code&gt; is called inside the function &lt;code&gt;bar()&lt;/code&gt;, the value of &lt;code&gt;x&lt;/code&gt; that was captured what determined by where &lt;code&gt;foo()&lt;/code&gt; was written. As at creation of function of the function &lt;code&gt;foo()&lt;/code&gt;, x is 1. Even though foo() is called inside bar(), a value x = 2 exists there, it doesn’t matter. As we said earlier: “It does not matter where the function is called, only where it was defined.”&lt;/p&gt;

&lt;p&gt;Even though &lt;code&gt;foo()&lt;/code&gt; is called inside &lt;code&gt;bar()&lt;/code&gt;, a value x = 2 exists there, it doesn’t matter. As we said earlier: “It does not matter where the function is called, only where it was defined.”&lt;/p&gt;

&lt;p&gt;A dynamic scoping just does the opposite of lexical scoping, that can kind of scoping technique is found in "ancient" programming languages like Lisp.&lt;/p&gt;

&lt;p&gt;Now that we understand the scoping technique javascript uses, let's talk about the scope of javascript variable &lt;strong&gt;var&lt;/strong&gt;, &lt;strong&gt;let&lt;/strong&gt; and &lt;strong&gt;const&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;var&lt;/strong&gt; - is function-scoped (or global if declared outside a function) variable and can be accessed anywhere inside the function it was declared.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;let&lt;/strong&gt; / &lt;strong&gt;const&lt;/strong&gt; - are block-scoped, only exists inside { } where declared.&lt;/p&gt;

&lt;p&gt;Let's visualise this with an example:&lt;/p&gt;

&lt;p&gt;Example 4:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function testVar() {
   if (true) {
      var x = 10;
      let y = 15
   }
    console.log(x); // 10 → accessible outside the block to the whole function (function-scoped) 
    console.log(y); // throws a ReferenceError: y is not defined
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how x logs a 10 in the console? This is because even though it is defined in a block (if statement), it's scope is the function and it's accessible everywhere in the function &lt;code&gt;testVar()&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;For &lt;strong&gt;let&lt;/strong&gt; on the other hand, it throws a &lt;code&gt;ReferenceError&lt;/code&gt; because it's scope is confined to the block (if statement) therefore, &lt;code&gt;console.log&lt;/code&gt; can't seem to find (reference) it. &lt;strong&gt;const&lt;/strong&gt; is similar to &lt;strong&gt;let&lt;/strong&gt;, just that is only holds a constant value and cannot be reassigned a new value unlike &lt;strong&gt;let&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Remember how we are able to access &lt;strong&gt;var&lt;/strong&gt; in the function scope even though it was declared inside a block? That takes us to the next concept I would be talking about, &lt;strong&gt;Hoisting&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Hoisting: JavaScript’s Magic Trick&lt;/strong&gt;&lt;br&gt;
Do you know that before your code runs, JavaScript secretly moves declarations to the top of their scope. This is called hoisting. All variable types and functions are hoisted in JavaScript, how they are done is what differs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;var&lt;/strong&gt; → hoisted and initialised to undefined.&lt;br&gt;
&lt;strong&gt;let&lt;/strong&gt; / &lt;strong&gt;const&lt;/strong&gt; → hoisted but cannot be accessed yet (Temporal Dead Zone)&lt;br&gt;
Function declarations → fully hoisted (body included)&lt;br&gt;
Function expressions → only the variable is hoisted&lt;/p&gt;

&lt;p&gt;Let's break down the above with examples.&lt;/p&gt;

&lt;p&gt;Example 5: &lt;strong&gt;var&lt;/strong&gt; hoisting&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(a); // undefined
var a = 5;
console.log(a); // 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What happens internally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var a; // declaration is hoisted (moved to the top of it's scope)
console.log(a); // undefined (no value yet)
a = 5;  // initialization happens here
console.log(a); // 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;var&lt;/strong&gt; declarations are hoisted &lt;strong&gt;and initialized with &lt;code&gt;undefined&lt;/code&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example 6: Hoisting with &lt;strong&gt;let&lt;/strong&gt; and &lt;strong&gt;const&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;console.log(b); // ReferenceError
let b = 10;

console.log(c); // ReferenceError
const c = 20;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;let&lt;/strong&gt; and &lt;strong&gt;const&lt;/strong&gt; are &lt;strong&gt;hoisted&lt;/strong&gt; too, but they are in a &lt;strong&gt;temporal dead zone (TDZ)&lt;/strong&gt; until the line where they are declared. Accessing them before the declaration &lt;strong&gt;throws ReferenceError&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example 6: Hoisting with functions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;greet(); // "Hello"
function greet() {
    console.log("Hello");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The entire function (declaration + body) is hoisted. You can call it &lt;strong&gt;before its declaration&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example 7: Hoisting with function expressions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello // undefined
hello(); // TypeError: hello is not a function
var hello = function() { console.log("Hi"); };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only the variable &lt;code&gt;hello&lt;/code&gt; is hoisted (as &lt;code&gt;undefined&lt;/code&gt;) &lt;strong&gt;not the function body&lt;/strong&gt; because it is defined as a &lt;strong&gt;var&lt;/strong&gt; (remember what we said about &lt;strong&gt;var&lt;/strong&gt; hoisting in e.g 5). So calling it early throws an error.&lt;/p&gt;

&lt;p&gt;Example 8:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hello // Reference Error 
hello(); // Reference Error
const hello = function() { console.log("Hi"); };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This just extends what happens with &lt;strong&gt;let&lt;/strong&gt; / &lt;strong&gt;const&lt;/strong&gt; because it's a function expression. What happens with function expression depends on the type of variable.&lt;/p&gt;

&lt;p&gt;Hoisting explains why some code “seems to exist before it’s declared.” Think of it as JavaScript sneakily rearranging your furniture before you enter the room. Finally, let's talk about closures.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Closures: Functions That Never Forget&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A closure is a function that remembers variables from the scope where it was created, even after that scope is gone. Closures are created by functions, exist inside functions, and are about how functions capture variables. &lt;/p&gt;

&lt;p&gt;Closures are why your setTimeout callbacks could remember &lt;code&gt;i&lt;/code&gt; differently depending on &lt;strong&gt;var&lt;/strong&gt; vs &lt;strong&gt;let&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Example 10: Counter Closure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function createCounter() {
  let count = 0;

  return function() {
    count++;
    console.log(count);
  };
}

const counter = createCounter();
counter(); // 1
counter(); // 2
counter(); // 3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Closure is what allows the function &lt;code&gt;counter()&lt;/code&gt; to still be able to access &lt;code&gt;count&lt;/code&gt; declared in &lt;code&gt;createCounter&lt;/code&gt; even after it has been called.&lt;/p&gt;

&lt;p&gt;Let's take a look at closure in loops and go back to genesis of everything, e.g 1 &amp;amp; 2.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Example 1
for (var i = 1; i &amp;lt;= 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 1000); // 4 4 4
}

// Loop start
// i = 1 → schedule callback #1
// i = 2 → schedule callback #2
// i = 3 → schedule callback #3
// Loop ends, i becomes 4

// Essentials, var uses the same variable (i) all through. 
// Returns the same i that is already 4


for (let i = 1; i &amp;lt;= 3; i++) {
  setTimeout(() =&amp;gt; console.log(i), 1000); // 1 2 3
}

/ Loop start
// Closure #1 → creates a new variable i1 → 1
// Closure #2 → creates a new variable i2 → 2
// Closure #3 → creates a new variable i3 → 3

// let creates stores a new variable (i1, i2, i3) in memory for each loop
//therefore each iteration has it's own i
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. Connecting the Dots&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Lexical scoping → decides which variables a function can see&lt;br&gt;
Hoisting → moves declarations to the top, creating surprises if you’re not careful&lt;br&gt;
Closures → allow functions to “remember” variables, leading to powerful patterns or head-scratching bugs&lt;/p&gt;

&lt;p&gt;Master these three concepts, and suddenly JavaScript’s “tricky snippets” start to feel predictable.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Conclusion&lt;/strong&gt;&lt;br&gt;
JavaScript loves to surprise us, but understanding scoping, hoisting, and closures gives you superpowers:&lt;/p&gt;

&lt;p&gt;You’ll predict outputs accurately&lt;br&gt;
You’ll write cleaner, safer code&lt;br&gt;
You’ll stop wondering if JS is secretly messing with you&lt;/p&gt;

&lt;p&gt;And remember: JavaScript isn’t trying to confuse you… it’s just teaching you to respect its rules. 😉&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>scope</category>
      <category>programming</category>
      <category>node</category>
    </item>
  </channel>
</rss>
