Variables: var, let, const & Scope
JS-এর সবচেয়ে গুরুত্বপূর্ণ একক নিয়ম
1. Three Ways to Declare a Variable
Modern JS gives you three keywords. Only two of them should appear in your code in 2026.
| Keyword | Reassignable? | Scope | Hoisted as | Use |
|---|---|---|---|---|
const | No | Block | uninitialized (TDZ) | Default |
let | Yes | Block | uninitialized (TDZ) | When you must reassign |
var | Yes | Function | undefined | Never (legacy only) |
const by default. Switch to let only when reassignment is required. Never write var in new code.
var ব্যবহার করবেন না। সর্বদা const default — শুধু পুনরায় assign করতে হলে let।2. The var Trap — Function Scope & Hoisting
var is function-scoped: a var declared inside an if or for leaks to the entire enclosing function. It is also hoisted as undefined — accessible before the line you wrote.
// Hoisted as undefined — no error before declaration
console.log(x); // undefined
var x = 5;
console.log(x); // 5
// Function-scoped: leaks out of the if block
function demo() {
if (true) {
var leak = "oops";
}
console.log(leak); // "oops" ← still accessible!
}
demo();
3. let & const — Block Scope
Block scope means a variable declared inside { } is invisible outside it. This matches how almost every other modern language behaves.
{
const a = 10;
let b = 20;
console.log(a, b); // 10 20
}
// console.log(a); → ReferenceError
for (let i = 0; i < 3; i++) {
// each iteration gets its OWN i — closures friendly
}
// console.log(i); → ReferenceError
4. The Temporal Dead Zone (TDZ)
let/const are also "hoisted" — but unlike var, they sit in the TDZ from the start of the block until the actual declaration. Accessing them in the TDZ is a hard error.
try {
console.log(y); // throws ReferenceError
} catch (e) {
console.log("caught:", e.message);
}
let y = 5;
console.log(y); // 5
let/const declare করা আগেই access করতে গেলে error। var-এর মতো undefined দেয় না — এটি ভালো, কারণ silent bug-কে prevent করে।5. const — What's Actually Constant?
const means the binding can't be reassigned. The value can still mutate if it's an object.
const n = 5;
// n = 6; → TypeError
const arr = [1, 2];
arr.push(3); // allowed — content mutated
console.log(arr); // [1, 2, 3]
// arr = [9]; → TypeError
const obj = { x: 1 };
obj.x = 99; // allowed
obj.y = 100; // allowed
console.log(obj); // { x: 99, y: 100 }
// True deep-immutable: Object.freeze (shallow only)
const frozen = Object.freeze({ a: 1 });
frozen.a = 2;
console.log(frozen.a); // 1 (silently ignored in non-strict)
6. Lexical Scope & Scope Chain
Lexical means scope is determined by where the code is written, not where it's called from. An inner function can see all variables of every outer function it is nested in — that chain is the scope chain.
const country = "Bangladesh"; // outer
function outer() {
const city = "Dhaka"; // middle
function inner() {
const area = "Mirpur"; // inner
console.log(area, city, country);
}
inner();
}
outer(); // Mirpur Dhaka Bangladesh
7. Global Scope Dangers
Anything declared at the top level without any keyword (or with var) becomes a property of the global object (window in browsers). This is the source of countless conflicts in the wild.
"use strict"; or work inside an ES Module (which is automatically strict). In strict mode, undeclared assignments throw instead of silently creating globals.
8. Glossary (শব্দকোষ)
| Term | Meaning | বাংলায় |
|---|---|---|
var | Legacy keyword — function-scoped, hoisted as undefined. | পুরোনো keyword — function-scoped এবং hoisting bug-প্রবণ। |
let | Block-scoped, reassignable variable. TDZ-protected. | Block-scoped, পুনরায় assign করা যায়; TDZ-সুরক্ষিত। |
const | Block-scoped binding that cannot be reassigned. | Block-scoped, পুনরায় assign করা যায় না। |
| Hoisting | Declarations are conceptually moved to the top of their scope. | Declaration বুঝে নিয়ে scope-এর শুরুতে সরানোর engine-এর কৌশল। |
| TDZ | Temporal Dead Zone — accessing let/const before declaration throws. | Declaration-এর আগে let/const access করলে error। |
| Block scope | Variables only visible inside the { } they were declared in. | যে { }-এ declare হয়েছে শুধু সেখানেই দৃশ্যমান। |
| Lexical scope | Scope determined by where the code is written, not called. | Code কোথায় লেখা হয়েছে তার উপর scope নির্ভর করে। |
| Strict mode | "use strict" — enables stricter parsing and error rules. | "use strict" — কঠোর parsing ও error নিয়ম। |
const default; পুনরায় assign করতে হলে let; var কখনোই না। Block scope এবং TDZ আপনার silent bug ধরিয়ে দেয়। ES module-এ strict mode automatically চালু থাকে — তাই module-এ "use strict" লেখার দরকার নেই।
9. Practice Problems
-
Show that
varhoists asundefinedbutletthrows.✨ Show Answer
a1.jsconsole.log(a); // undefined var a = 10; try { console.log(b); } catch (e) { console.log("err:", e.message); } let b = 20; -
Demonstrate that
conston an array still allowspush.✨ Show Answer
a2.jsconst arr = [1, 2]; arr.push(3); arr[0] = 9; console.log(arr); // [9, 2, 3] -
Predict the output: a
forloop withvar+setTimeoutprintingi. Then fix it withlet.✨ Show Answer
With
var, every callback closes over the samei(final value 3). Withlet, each iteration gets a fresh binding.a3.jsfor (var i = 0; i < 3; i++) setTimeout(() => console.log("var:", i), 0); for (let j = 0; j < 3; j++) setTimeout(() => console.log("let:", j), 0); -
Write a function that creates an inner counter using lexical scope.
✨ Show Answer
a4.jsfunction makeCounter() { let n = 0; return () => ++n; } const c = makeCounter(); console.log(c(), c(), c()); // 1 2 3 -
Use
Object.freezeto make a config object truly immutable.✨ Show Answer
a5.js"use strict"; const CONFIG = Object.freeze({ apiUrl: "/api", retries: 3 }); try { CONFIG.retries = 5; } catch (e) { console.log("blocked:", e.message); } console.log(CONFIG); -
Show that
letin aforloop creates one binding per iteration.✨ Show Answer
a6.jsconst fns = []; for (let k = 0; k < 3; k++) { fns.push(() => k); } console.log(fns.map(f => f())); // [0, 1, 2] -
In one paragraph, explain why
varshould never appear in modern code.✨ Show Answer
Answer:
varis function-scoped, hoisted asundefined, and silently re-declarable — three behaviours that conspire to produce ghost bugs that only show up far from where the variable was written.letandconstare block-scoped, throw on TDZ access, and refuse silent re-declaration, so most accidents become visible at the line that caused them. Linters and toolchains in 2026 default to forbiddingvar.varfunction-scoped, hoisted-as-undefined এবং re-declarable — তিনটি বৈশিষ্ট্য একসাথে এমন bug তৈরি করে যা declaration থেকে অনেক দূরে দেখা দেয়।let/constএসব সমস্যা দূর করে। -
Show that
letin a block is invisible outside.✨ Show Answer
a8.js{ let hidden = 42; console.log("inside:", hidden); } try { console.log(hidden); } catch (e) { console.log("outside:", e.message); } -
Predict the output of three nested functions accessing an outer
const.✨ Show Answer
a9.jsconst g = "global"; function a() { const m = "middle"; function b() { const i = "inner"; console.log(g, m, i); } b(); } a(); // global middle inner -
Show that re-declaring
let xin the same scope is a SyntaxError.✨ Show Answer
You can't catch a SyntaxError at runtime — it's detected at parse time. Try copying this into the browser console:
a10.js// SyntaxError when parsed: // let z = 1; // let z = 2; // var allows it (silently!): var w = 1; var w = 2; console.log(w); // 2 — silent overwrite, classic var bug -
Build a tiny "module" using an IIFE and a closure to keep state private.
✨ Show Answer
a11.jsconst wallet = (() => { let balance = 0; return { deposit(n) { balance += n; }, balanceOf() { return balance; } }; })(); wallet.deposit(100); wallet.deposit(50); console.log(wallet.balanceOf()); // 150 console.log(wallet.balance); // undefined (private) -
Why is "use strict" usually unnecessary inside an ES module?
✨ Show Answer
Answer: ES Modules are strict by default. Anything loaded with
<script type="module">orimported in Node runs in strict mode automatically — silent globals throw,thisisundefinedat the top level, and reserved words can't be used as identifiers.ES module-এ
"use strict"লেখার দরকার নেই — module-এ এটি default-ভাবেই enabled থাকে।
Summary — Module 06
Use const by default, let when reassignment is required, never var. let and const are block-scoped and protected by the TDZ. Lexical scope means inner functions see all enclosing variables. Always work in strict mode (or ES modules, which are strict by default).
const default, let দরকারে, var কখনোই না। Block scope এবং TDZ দুই-ই আপনার মিত্র।