Biến Block-Scoped với let và const (ES2015)
Trước ES2015, JavaScript chỉ có var (function-scoped). ES6 giới thiệu let và const cho block scope (phạm vi khối) - biến chỉ tồn tại trong cặp { } gần nhất.
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Block Scoped Variables</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; }
.result, .sample { font-size: 18px; font-weight: 500; font-family: monospace; }
.result { color: green; }
button { padding: 8px 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Khai báo biến Block-Scoped</h1>
<div class="sample"></div>
<div class="result"></div>
<button class="btn-demo">KIỂM TRA PHẠM VI</button>
<h3>Nhấn nút để khai báo và kiểm tra biến block-scoped</h3>
<script>
const resEle = document.querySelector(".result");
const sampleEle = document.querySelector(".sample");
const btn = document.querySelector(".btn-demo");
// Khối lệnh { } tạo block scope mới
{
let a = 22;
const b = 44;
sampleEle.textContent = `Trong block:\nlet a = ${a}\nconst b = ${b}`;
}
// a và b KHÔNG truy cập được ở đây
btn.addEventListener("click", () => {
try {
// Tham chiếu a, b ngoài block -> ReferenceError
const sum = a + b;
resEle.textContent = `a + b = ${sum}`;
} catch (err) {
resEle.textContent = `Lỗi: ${err.message}\n(Biến a, b chỉ tồn tại trong block { })`;
}
});
</script>
</body>
</html>
So sánh var vs let/const - Phạm vi
function demo() {
if (true) {
var x = 1; // function-scoped
let y = 2; // block-scoped
const z = 3; // block-scoped
}
console.log(x); // 1 - var "rò rỉ" ra ngoài block
// console.log(y); // ReferenceError - let bị chặn bởi block
// console.log(z); // ReferenceError - const bị chặn bởi block
}
Ví dụ thực tế: Vòng lặp
// Vấn đề với var
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log('var:', i), 100); // In ra: 3, 3, 3
}
// Giải quyết với let (mỗi lần lặp tạo binding mới)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log('let:', i), 100); // In ra: 0, 1, 2
}
// const trong vòng lặp for...of
for (const item of ['a', 'b', 'c']) {
console.log(item); // OK, binding mới mỗi lần lặp
}
// for (const i = 0; i < 3; i++) {} // LỖI: const không thể gán lại i++
Temporal Dead Zone (TDZ)
Khoảng từ đầu block đến dòng khai báo let/const gọi là TDZ. Truy cập biến trong TDZ ném ReferenceError:
{
console.log(x); // ReferenceError
let x = 10;
}
Best Practice
- Luôn dùng
constmặc định. - Dùng
letkhi cần gán lại. - Khai báo biến ở đầu block (hoặc ngay trước khi dùng) để tránh TDZ.
- Dùng block
{ }tường minh để giới hạn scope khi cần.