Tạm biệt var…

  • Phuong Dang
  • 07/Sep/2022
  1. Khai báo biến với var
  2. Khai báo biến với let
  3. Khai báo biến với const
  4. Kết luận.

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é.

1. Khai báo biến với var

1.1 var scope

Với var, khi biến được tạo ra thì có scope là: "execution context".

  • Nếu biến được khai báo bên ngoài tất cả các function thì scope sẽ là global scope.
  • Nếu biến được khai báo trong function thì scope sẽ là function scope. Biến có thể sử dụng trong function và tất cả nested functions.
 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 iuserName 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.

1.2 var hoisting

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'

1.3 var re-declaring

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.

2. Khai báo biến với let

Với let, khi biến được tạo ra thì có scope phụ thuộc vào block được khai báo.

  • Nếu biến được khai báo bên ngoài tất cả các function thì scope sẽ là global scope.
  • Nếu biến được khai báo trong function thì scope sẽ là block scope. Biến sẽ chỉ có thể sử dụng trong phạm vi của block và tất cả nested block được khai báo. Block ở đây có thể là cả function hoặc là if/loop.
 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ì iuserName 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

3. Khai báo biến với const

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:

  • Biến được tạo với const thì bắt buộc phải gán giá trị khi khởi tạo
  • Biến được tạo với const 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.

4. Kết luận

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.

  • Sử dụng const ở bất kỳ nơi nào mà biến không cần gán lại giá trị.
  • Sử dụng let ở bất kỳ nơi nào mà biến có thể gán lại giá trị.
  • Ngừng sử dụng 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.