Trong JavaScript, khai báo lại biến cùng tên với var không gây lỗi và không reset giá trị. Đây là hành vi cho phép nhưng có thể dẫn đến bug nếu không cẩn thận.
Hành vi với var
var age = 20;
var age; // Khai báo lại - KHÔNG LÀM GÌ (giá trị giữ nguyên)
console.log(age); // 20
var age = 30; // Khai báo + gán - GIÁ TRỊ THAY ĐỔI
console.log(age); // 30
Quy tắc: var name; đơn lẻ (không gán giá trị) là no-op nếu biến đã tồn tại trong scope. Chỉ var name = value; mới gán giá trị mới.
Ví dụ từ bài gốc
<script>
var age = 20;
var age; // Không thay đổi age
if (age > 18) {
document.write("Đủ điều kiện lái xe"); // Chạy
}
</script>
Khác biệt với let và const (ES6)
let x = 1;
let x = 2; // SyntaxError: Identifier 'x' has already been declared
const y = 1;
const y = 2; // SyntaxError
// Trong block khác thì OK
let a = 1;
{
let a = 2; // OK - block scope khác
console.log(a); // 2
}
console.log(a); // 1
Function Scope vs Block Scope
// var: function scope
function foo() {
var x = 1;
if (true) {
var x = 2; // Cùng biến!
console.log(x); // 2
}
console.log(x); // 2 (biến outer bị thay đổi)
}
// let/const: block scope
function bar() {
let y = 1;
if (true) {
let y = 2; // Biến mới, shadowing
console.log(y); // 2
}
console.log(y); // 1 (biến outer không đổi)
}
Tại sao var cho phép re-declare?
- Thiết kế ban đầu cho script nhỏ, linh hoạt.
- Hoisting: nhiều
var xgộp thành một khai báo ở đầu scope. - Tương thích ngược (backward compatibility) – không thể bỏ.
Best Practice
- Luôn dùng
let/const– báo lỗi ngay khi trùng khai báo. - Bật
"use strict"hoặc ESLint ruleno-redeclare. - Tránh khai báo biến trùng tên trong cùng scope – gây nhầm lẫn.
- Dùng block
{ }để tạo scope cục bộ cho biến tạm.
Tóm tắt
| Từ khóa | Khai báo lại cùng scope | Gán lại giá trị |
|---|---|---|
var | Cho phép (chỉ khai báo: no-op) | Cho phép |
let | Cấm (SyntaxError) | Cho phép |
const | Cấm (SyntaxError) | Cấm (TypeError) |