404 Not Found

404 Not Found


nginx

JavaScript Scope

Scope determines where a variable can be accessed. Think of it like an ID card — your city ID only works within your province, and your provincial ID won't help you abroad. Variables work the same way: once you leave their scope, they're unreachable.

📖 Summary

Global Scope

Variables declared with var, let, or const at the outermost level have global scope and can be accessed from anywhere.

HTML
<script>
var globalVar = "I'm a global variable";
function test() {
  console.log(globalVar); // Accessible
}
test();
</script>

Global variables are convenient but can easily pollute the namespace — as projects grow, naming conflicts become inevitable. Minimize global variable usage.

Function Scope

Variables declared with var follow function scope: they only exist within the current function and disappear outside it.

HTML
<script>
function greet() {
  var message = "Hello";
  console.log(message); // Works fine
}
greet();
try {
  console.log(message); // ReferenceError!
} catch(e) {
  console.log("Access failed: " + e.message);
}
</script>

A classic gotcha with var's function scope: using var in a for loop means the variable "leaks" out after the loop ends.

Block Scope

Variables declared with let and const follow block scope: they only exist within the current {}.

HTML
<script>
if (true) {
  let x = 10;
  const y = 20;
  var z = 30;
  console.log("Inside block: x=" + x + ", y=" + y + ", z=" + z);
}
console.log("Outside block: z=" + z); // var isn't restricted by blocks
try {
  console.log(x); // x is not accessible here
} catch(e) {
  console.log("x not accessible: " + e.message);
}
</script>

This is why loop counters should use let instead of var.

Scope Chain

Inner scopes can access variables from outer scopes — like how you can use things from home at school (if you brought them). When looking up a variable, JavaScript searches from the current scope outward, layer by layer, until it reaches the global scope. This chain is called the scope chain.

HTML
<script>
const city = "Beijing";
function outer() {
  const district = "Haidian";
  function inner() {
    const street = "Zhongguancun";
    console.log(street, district, city); // All accessible
  }
  inner();
}
outer();
</script>

Outer scopes cannot access inner scope variables — just like you can't reach into a student's pocket from outside the classroom.

Hoisting

var declarations are "hoisted" to the top of their scope, but assignments are not:

HTML
<script>
console.log(a); // undefined (not an error!)
var a = 5;
// Actual execution order: var a; → console.log(a); → a = 5;
</script>

let and const are also hoisted, but accessing them before declaration throws an error — this region is called the Temporal Dead Zone (TDZ):

HTML
<script>
try {
  console.log(b); // ReferenceError!
} catch(e) {
  console.log("Temporal dead zone: " + e.message);
}
let b = 5;
</script>

Bottom line: always use let and const; avoid the hoisting pitfalls of var.

Closures Explained Intuitively

A closure = a function + the variable environment where it was born. It's like someone who left their hometown but still remembers what it looked like — even if the hometown changed later, their memory of it stays the same.

HTML
<script>
function createCounter() {
  let count = 0;        // This variable is "locked" inside the closure
  return function() {
    count++;
    return count;
  };
}

const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
// The count variable wasn't destroyed because the returned function still "remembers" it
</script>

createCounter has already finished executing — logically, count should be destroyed. But the returned function holds tightly onto count, so it survives. That's a closure.

Practical Closure Use Cases


Example: var vs let in Loops

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>var vs let</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .block { margin: 16px 0; padding: 12px; background: #f5f5f5; border-radius: 6px; }
    .block h3 { margin-top: 0; }
    .var-result, .let-result { margin: 8px 0; font-weight: bold; }
    .var-result { color: #d9534f; }
    .let-result { color: #5cb85c; }
  </style>
</head>
<body>
  <h2>var vs let: Scope Differences in Loops</h2>
  <div id="output"></div>
  <script>
    const varResults = [];
    const letResults = [];

    for (var i = 0; i < 3; i++) {
      varResults.push(`Inside loop: i = ${i}`);
    }
    varResults.push(`Outside loop: i = ${i} (var leaked!)`);

    for (let j = 0; j < 3; j++) {
      letResults.push(`Inside loop: j = ${j}`);
    }
    letResults.push(`Accessing j outside loop → ReferenceError (let doesn't leak)`);

    document.getElementById("output").innerHTML = `
      <div class="block">
        <h3>var Declaration</h3>
        ${varResults.map(r => `<div class="var-result">${r}</div>`).join("")}
      </div>
      <div class="block">
        <h3>let Declaration</h3>
        ${letResults.map(r => `<div class="let-result">${r}</div>`).join("")}
      </div>
    `;
  </script>
</body>
</html>
▶ Try it Yourself

Example: Scope Chain Demo

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Scope Chain</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .scope { margin: 12px 0; padding: 12px; border-left: 4px solid #4a90d9; background: #f0f7ff; border-radius: 0 6px 6px 0; }
    .scope-inner { margin: 12px 0 12px 20px; padding: 12px; border-left: 4px solid #5cb85c; background: #f0fff0; border-radius: 0 6px 6px 0; }
    .scope-innermost { margin: 12px 0 12px 20px; padding: 12px; border-left: 4px solid #f0ad4e; background: #fffdf0; border-radius: 0 6px 6px 0; }
    code { background: rgba(0,0,0,0.06); padding: 2px 6px; border-radius: 3px; }
  </style>
</head>
<body>
  <h2>Scope Chain: Looking Up Variables from Inside Out</h2>
  <div id="output"></div>
  <script>
    const country = "USA";

    function outer() {
      const state = "California";

      function middle() {
        const city = "San Francisco";

        function inner() {
          const district = "Mission";
          const result = `${district}, ${city}, ${state}, ${country}`;
          return result;
        }

        return inner();
      }

      return middle();
    }

    const final = outer();

    document.getElementById("output").innerHTML = `
      <div class="scope">
        <strong>Global Scope</strong>: country = <code>"${country}"</code>
        <div class="scope-inner">
          <strong>outer Function Scope</strong>: state = <code>"California"</code>
          <div class="scope-innermost">
            <strong>middle Function Scope</strong>: city = <code>"San Francisco"</code>
            <div style="margin-top:12px; padding:12px; border-left:4px solid #d9534f; background:#fff0f0; border-radius:0 6px 6px 0;">
              <strong>inner Function Scope</strong>: district = <code>"Mission"</code>
              <p>Innermost scope can access all outer variables → <strong>${final}</strong></p>
            </div>
          </div>
        </div>
      </div>
    `;
  </script>
</body>
</html>
▶ Try it Yourself

Example: Closure Counter with Private Variables

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Closures</title>
  <style>
    body { font-family: sans-serif; padding: 20px; text-align: center; }
    .counter { display: inline-block; padding: 20px; background: #f0f7ff; border-radius: 12px; margin: 12px; }
    .count { font-size: 48px; font-weight: bold; color: #4a90d9; }
    button { padding: 10px 24px; font-size: 16px; border: none; border-radius: 6px; cursor: pointer; background: #4a90d9; color: #fff; margin: 4px; }
    button:hover { background: #357abd; }
    .reset { background: #d9534f; }
    .reset:hover { background: #c9302c; }
    .info { color: #888; font-size: 14px; margin-top: 8px; }
  </style>
</head>
<body>
  <h2>Closure Counter</h2>
  <div class="counter">
    <div class="count" id="display">0</div>
    <button id="increment">+1</button>
    <button id="decrement">-1</button>
    <button id="reset" class="reset">Reset</button>
    <div class="info">The count variable is protected by the closure — it can't be modified directly from outside</div>
  </div>
  <script>
    function createCounter() {
      let count = 0;
      return {
        increment() { return ++count; },
        decrement() { return --count; },
        reset() { count = 0; return count; },
        getCount() { return count; }
      };
    }

    const counter = createCounter();
    const display = document.getElementById("display");

    function updateDisplay() {
      display.textContent = counter.getCount();
    }

    document.getElementById("increment").addEventListener("click", function() {
      counter.increment();
      updateDisplay();
    });

    document.getElementById("decrement").addEventListener("click", function() {
      counter.decrement();
      updateDisplay();
    });

    document.getElementById("reset").addEventListener("click", function() {
      counter.reset();
      updateDisplay();
    });
  </script>
</body>
</html>
▶ Try it Yourself

Example: Hoisting Comparison

HTML
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Hoisting</title>
  <style>
    body { font-family: sans-serif; padding: 20px; }
    .compare { display: flex; gap: 20px; margin: 16px 0; }
    .col { flex: 1; padding: 16px; border-radius: 8px; }
    .col-var { background: #fff0f0; border: 2px solid #d9534f; }
    .col-let { background: #f0fff0; border: 2px solid #5cb85c; }
    h3 { margin-top: 0; }
    code { background: rgba(0,0,0,0.06); padding: 2px 6px; border-radius: 3px; }
    pre { background: #fff; padding: 12px; border-radius: 6px; overflow-x: auto; }
  </style>
</head>
<body>
  <h2>Hoisting: var vs let</h2>
  <div id="output"></div>
  <script>
    var varBefore = "Access before var declaration → " + typeof aVar;
    var aVar = 5;
    var varAfter = "Access after var declaration → " + aVar;

    let letAfter = 5;
    let letBefore = "Access before let declaration → throws ReferenceError!";

    document.getElementById("output").innerHTML = `
      <div class="compare">
        <div class="col col-var">
          <h3>var (hoisted but not assigned)</h3>
          <pre><code>console.log(typeof aVar);  // ${varBefore}
var aVar = 5;
console.log(aVar);         // ${varAfter}</code></pre>
          <p>No error, but you get <code>undefined</code> — a more subtle bug!</p>
        </div>
        <div class="col col-let">
          <h3>let (temporal dead zone)</h3>
          <pre><code>console.log(bVar);  // ${letBefore}
let bVar = 5;</code></pre>
          <p>Throws an error immediately — you catch the problem right away!</p>
        </div>
      </div>
    `;
  </script>
</body>
</html>
▶ Try it Yourself

❓ FAQ

Q Why does using var in a for loop cause all callbacks to output the same value?
A Because var has function scope — after the loop ends, there's only one i, and all callbacks share it (which by then equals the loop's final value). With let, each iteration gets its own independent copy of i.
Q Do closures cause memory leaks?
A Closures themselves aren't memory leaks — they intentionally maintain references to variables. A real leak happens when you no longer need a closure, but it's still held by another reference chain, preventing the variable from being garbage collected. The fix: set the reference to null.
Q Should I use let or const?
A Default to const; only use let when you need to reassign. This habit prevents you from accidentally modifying variables that shouldn't change.

📝 Exercises

  1. Write a function makeGreeter(greeting) that returns a function which, when called with a name, returns greeting + ", " + name. Create two greeting functions with different greetings and verify they don't interfere with each other.
  2. Write a createWallet(initialBalance) function that returns three methods: deposit, withdraw, and getBalance. The balance should not be directly accessible from outside (private variable) — it can only be manipulated through these methods.
  3. Explain the output of the following code and why:
HTML
<script>
for (var i = 0; i < 3; i++) {
  setTimeout(function() { console.log(i); }, 100);
}
</script>

Then modify it to output 0, 1, 2.

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏