Var trong JavaScript và cách sử dụng IIFE

Cập nhật ngày 27/12/2021

Var trong JavaScript là một cách cũ để khai báo biến. Và bạn không nên sử dụng var nữa, mà thay vào đó là sử dụng let hoặc const.

Tuy nhiên, var vẫn tồn tại trong nhiều mã nguồn cũ. Việc nghiên cứu về var giúp bạn hiểu được những mã nguồn này. Qua đó, bạn biết cách để có thể chuyển từ var sang let/const.

Sau đây là những đặc điểm cơ bản của var trong JavaScript.

var không có phạm vi block

Biến được khai báo bằng var, hoặc là có phạm vi toàn cục hoặc là có phạm vi hàm, khác với phạm vi block của biến sử dụng let/const.

Ví dụ sử dụng var trong JavaScript:

if (true) {
  var test = true; // sử dụng "var" thay vì "let"}

console.log(test); // true - biến test vẫn nhìn thấy ở ngoài if

Trong ví dụ trên, biến khai báo với varphạm vi toàn cục và được nhìn thấy ở ngoài block.

Nếu bạn sử dụng let test thay vì var test, thì biến test chỉ được nhìn thấy trong block của câu lệnh if:

if (true) {
  let test = true; // sử dụng "let" thay vì "var"}

console.log(test); // Uncaught ReferenceError: test is not defined

Tương tự khi sử dụng var trong vòng lặp for:

for (var i = 0; i < 10; i++) {  var a = 1;  // ...
}

console.log(i); // 10, biến i vẫn được nhìn thấy sau for, i là biến toàn cục
console.log(a); // 1, biến a vẫn được nhìn thấy sau for, a là biến toàn cục

Nếu khối code nằm trong hàm thì biến khai báo sử dụng varphạm vi hàm, ví dụ:

function sayHi() {
  if (true) {
    var message = "Hello";  }

  console.log(message); // Hello
}

sayHi();
console.log(message); // Uncaught ReferenceError: message is not defined

Trong ví dụ trên, biến message được khai báo sử dụng var. Biến này có thể nhìn thấy sau khối code của if nhưng không được nhìn thấy ở ngoài hàm sayHi.

Nói cách khác, biến message chỉ có phạm vi hàm.

Có thể khai báo lại biến với var

Nếu bạn khai báo biến hai lần với let ở cùng một phạm vi thì sẽ có lỗi xảy ra:

let user;
let user; // SyntaxError: 'user' has already been declared

Với var thì khác, bạn có thể khai báo lại biến với số lần tùy ý:

var user = "Alex";
var user = "Anna";

console.log(user); // Anna

Có thể sử dụng biến trước khi khai báo với var

Biến khai báo sử dụng var được xử lý khi hàm bắt đầu (hoặc bắt đầu chương trình với phạm vi toàn cục), ví dụ:

function sayHi() {
  message = "Hello";

  console.log(message);

  var message;}

sayHi(); // Hello

Đoạn code trên không có lỗi và tương đương với cách viết sau:

function sayHi() {
  var message;
  message = "Hello";

  console.log(message);
}

sayHi(); // Hello

Hoặc thậm chí là (nhớ rằng biến với var không có phạm vi block):

function sayHi() {
  message = "Hello";

  if (false) {
    var message;  }

  console.log(message);
}

sayHi(); // Hello

Việc có thể sử dụng biến với var trước khi khai báo được gọi là hoisting. Nghĩa là tất cả biến được khai báo với var sẽ được đưa lên đầu của hàm.

Trong đoạn code trên, nhánh if (false) không bao giờ được thực hiện. Nhưng câu lệnh khai báo var message vẫn được đưa lên đầu hàm. Do đó, việc sử dụng biến message không bị lỗi.

Chú ý: Khai báo biến với var được hoisted nhưng lệnh gán thì không, ví dụ:

function sayHi() {
  console.log(message);

  var message = "Hello";}

sayHi(); // undefined

Dòng var message = "Hello" thực hiện hai hành động:

  • Khai báo biến var message.
  • Gán giá trị cho biến message = "Hello".

Phần khai báo biến được đưa lên đầu hàm, nhưng phần gán giá trị của biến vẫn giữ nguyên vị trí. Đó là lý do tại sao kết quả in ra là undefined.

Đoạn code trên tương đương với:

function sayHi() {
  var message;
  console.log(message);

  message = "Hello";}

sayHi(); // undefined

Cách sử dụng IIFE trong JavaScript

Trước đây, JavaScript chỉ dùng var để khai báo biến. Mà biến khai báo với var lại không có phạm vi block. Vì vậy, người ta đã phát minh ra cách để mô phỏng phạm vi block này.

Và cách đó gọi là IIFE, viết tắt của immediately-invoked function expressions.

Ví dụ một đoạn code sử dụng IIFE như sau:

(function () {
  var message = "Hello";

  console.log(message); // Hello
})();

Trong ví dụ trên, một biểu thức hàm được tạo ra và thực hiện ngay lập tức. Khi đoạn code trên thực hiện xong, không có cách nào để truy cập vào biến message từ phạm vi bên ngoài.

Đó chính là lợi ích khi sử dụng IFFE.

Với IFFE, biểu thức hàm được đặt trong cặp dấu ngoặc đơn (function(){...}). Bởi vì, JavaScript Engine hiểu từ khóa function dùng để khai báo hàm. Mà khai báo hàm thì cần phải có tên.

Do đó, khi mình bỏ dấu ngoặc đơn để khai báo IFFE thì sẽ bị lỗi:

// Định nghĩa IFFE mà không dùng dấu ngoặc đơn -> lỗi:
function() { // <-- SyntaxError: Function statements require a function name
  var message = "Hello";

  console.log(message);
}();

Thậm chí là khi viết thêm tên vào thì vẫn sẽ có lỗi:

function sayHi() {
  var message = "Hello";

  console.log(message);
}(); // -> JS không cho phép gọi hàm ngay khi khai báo

Việc đặt biểu thức hàm bên trong cặp dấu () để JavaScript hiểu rằng hàm được tạo ra ở một biểu thức khác. Vì vậy, biểu thức hàm trong trường hợp này không cần tên và có thể gọi ngay lập tức.

Ngoài ra, có một vài cách khác để khai báo IFFE như sau:

(function() {
  console.log("Dấu ngoặc đơn bao quanh function");
})();

(function() {
  console.log("Dấu ngoặc đơn bao quanh tất cả");
}());

!function() {
  console.log("Toán tử bitwise ! bắt đầu hàm");
}();

+function() {
  console.log("Toán tử + bắt đầu hàm");
}();

📝 Chú ý: với cú pháp JS hiện đại, bạn không nên (không cần thiết) viết code theo cú pháp như này.

Những cách viết trên chỉ nhằm mục đích giúp bạn hiểu về var, cách sử dụng var và IFFE trong JavaScript.

Để khi bạn gặp phải những mã nguồn cũ sử dụng var và IFFE, bạn vẫn có thể hiểu được logic chương trình và dễ dàng chuyển sang sử dụng let/const khi cần thiết.

Tổng kết

Có hai điểm chính khác nhau giữa varlet/const là:

  • Biến khai báo với var không có phạm vi block mà có phạm vi hàm hoặc toàn cục (nếu khai báo bên ngoài hàm).
  • Việc khai báo biến với var được thực hiện khi hàm bắt đầu (hoặc khi bắt đầu chương trình với biến toàn cục).

Điểm khác nhau khiến var trở nên lỗi thời và không được sử dụng trong lập trình JavaScript hiện đại.

★ Nếu bạn thấy bài viết này hay thì hãy theo dõi mình trên Facebook để nhận được thông báo khi có bài viết mới nhất nhé:

Closure là gì? Tìm hiểu closure trong JS
Đối tượng global trong JavaScript
Chia sẻ:

Bình luận