Bạn sẽ trả lời thế nào khi gặp câu hỏi "Dùng keyword nào để khai báo biến trong javascript?". Tôi đã thử hỏi học viên của mình và nhiều câu trả lời là: var
. Câu trả lời đó không sai nhưng tiêu đề của bài viết lại là "Tạm biệt var", hãy cùng nhau thảo luận tại sao lại vậy nhé.
Với var
, khi biến được tạo ra thì có scope là: "execution context".
1 2 3 4 5 6 7 8 9 10 11 12 13 | function test() { var siteName = 'Winzone.vn'; function nestedTest() { console.log(siteName); // Print "Winzone.vn" } nestedTest(); console.log(siteName); // Print "Winzone.vn" } test(); console.log(siteName); // Uncaught ReferenceError: siteName is not defined |
Biến siteName
được khai báo trong test() function nên có thể sử dụng ở bất kỳ đâu trong test() function và nestedTest() function nhưng bên ngoài function đó thì không thể. Mọi thứ đến đây vẫn bình thường, hãy cùng nhau xem 1 ví dụ nữa:
1 2 3 4 5 6 7 8 9 10 | function test() { for (var i = 0; i < 5; i++) { var userName = 'Phuong Dang'; } console.log(i); // Print "5" console.log(userName); // Print "Phuong Dang" } test(); |
Cả hai biến i
và userName
vẫn có thể dùng bên ngoài bên ngoài vòng lặp for
.
Với những người đã quen lập trình với java, c#... thì scope của biến sẽ chỉ được trong phạm vi của block {} biến đó được khai báo nên tư duy theo thói quen sẽ phán đoán 2 biến này sẽ không dùng bên ngoài for
block được.
Tuy nhiên cách hoạt động của var
là một khi đã khai báo trong function thì nó là biến của toàn bộ function (in the entire function).
Điều này khiến var
không được minh bạch cho lắm và vẫn chưa kết thúc vậy đâu. Hãy cùng tiếp tục xem var
không tường minh như nào.
Có một khái niệm gọi là "var hoisting" đó là: biến có thể xuất hiện trước khi nó được khai báo. Một điều không thể xuất hiện với khi khai báo biến ở các ngôn ngữ khác.
1 2 3 | blogName = 'Winzone.vn'; var blogName; console.log(blogName) // Print 'Winzone.vn' |
Đoạn source trên được hiểu tương tự như sau:
1 2 3 | var blogName; blogName = 'Winzone.vn'; console.log(blogName) // Print 'Winzone.vn' |
var
cũng cho phép ta có thể khai báo lại một biến trùng tên trong cùng scope đã được khai báo trước đó.
Đây cũng là một điều không thể xuất hiện khi khai báo biến ở các ngôn ngữ khác
1 2 | var siteName; var siteName; // Works fine. |
Với let
, khi biến được tạo ra thì có scope phụ thuộc vào block được khai báo.
1 2 3 4 5 6 7 8 9 10 11 12 13 | function test() { let siteName = 'Winzone.vn'; function nestedTest() { console.log(siteName); // Print "Winzone.vn" } nestedTest(); console.log(siteName); // Print "Winzone.vn" } test(); console.log(siteName); // Uncaught ReferenceError: siteName is not defined |
Kết quả chúng ta nhận được cũng giống hệt khi sử dụng với var
. Tiếp tục điều này với ví dụ 2:
1 2 3 4 5 6 7 8 9 10 | function test() { for (let i = 0; i < 5; i++) { let userName = 'Phuong Dang'; } console.log(i); // Uncaught ReferenceError: i is not defined console.log(userName); // Never executes } test(); |
Kết quả lúc này đã khác bởi vì i
và userName
không thể sử dụng vì chúng chỉ có giá trị trong vòng lặp for được khai báo. Wow, đó chính xác là điều chúng ta muốn.
Không hề có khái niệm "let hoisting" như "var hoisting". Bắt đầu start của block code cho đến khi biến được khởi tạo với let thì vùng đó được gọi là "temporal dead zone", tức là biến không thể được sử dụng trước khi khai báo với let.
1 2 3 4 5 | a = 1; // This works b = 2; // Uncaught ReferenceError: Cannot access 'b' before initialization var a; let b; |
Chúng ta cũng không thể re-declare biến trong cùng một scope khi sử dụng let
1 2 3 4 5 | let blogName; let blogName; // SyntaxError: Identifier 'blogName' has already been declared var winzone; let winzone; // SyntaxError: Identifier 'winzone' has already been declared |
Scope của biến khi khai báo với const
tương tự với let
, nó đều thuộc về block được khai báo.
Có 2 điểm khác biệt giữa const với var
, let
:
const
thì bắt buộc phải gán giá trị khi khởi tạoconst
thì không thể thay đổi giá trị.1 2 3 4 | const a; // Uncaught SyntaxError: Missing initializer in const declaration const b = 2; b=3; // Uncaught TypeError: Assignment to constant variable. |
Cần lưu ý rằng giá trị của biến const
thì không thể thay đổi, nhưng chúng ta hoàn toàn có thể thay đổi giá trị property của object, hoặc giá trị item của array được khai báo bằng const
1 2 3 4 5 6 | const user = { name: "PhuongDP", age: 28 }; user.age = 29; console.log(user); // {name: "PhuongDP", age: 29} |
1 2 3 4 5 | const MY_ARRAY = []; MY_ARRAY.push('A'); console.log(MY_ARRAY); // ["A"] MY_ARRAY = ['B']; // Uncaught TypeError: Assignment to constant variable. |
Sau đây là table tổng hợp 3 cách khai báo biến với var
, let
, const
:
# | var | let | const |
---|---|---|---|
origins | pre ES2015 | ES2015 (ES6) | ES2015 (ES6) |
scope | global scope or function scope | global scope or block scope | global scope or block scope |
hoisting | YES | NO | NO |
re-declaration within scope | YES | NO | NO |
re-assigned within scope | YES | YES | NO |
Bây giờ thì các bạn đã hiểu cách thức hoạt động của var
, let
, const
rồi. Vậy thì chúng ta sẽ lựa chọn keyword nào để khai báo biến trong javascript.
const
ở bất kỳ nơi nào mà biến không cần gán lại giá trị.let
ở bất kỳ nơi nào mà biến có thể gán lại giá trị.var
ngay lập tức bởi sự không minh bạch cũng như có thể mang lại nhiều kết quả không như ý.
Ngoại trừ trường hợp đặc biệt là dự án bạn làm cần phải support những browser chưa hỗ trợ ES6.Hi vọng rằng sau bài viết này chúng ta sẽ thực sự hiểu và sẽ có những sự lựa chọn đúng đắn khi khai báo biến trong javascript.