JavaScript: IIFE - Hàm gọi ngay


Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên

Tổng quan

IIFE (immediately invoked function expressions) là một hàm được định nghĩa dưới dạng một biểu thức và được thực thi ngay sau khi tạo. Phần sau đây trình bày cú pháp xác định một biểu thức hàm được gọi ngay lập tức:

(function(){
    //...
})();

Mục đích sử dụng IIFE

Khi bạn xác định một hàm , JavaScript engine sẽ thêm hàm đó vào đối tượng chung. Xem ví dụ sau:

function add(a,b) {
    return a + b;
}

Trong trình duyệt web, JavaScript engine thêm add() vào đối tượng chung window:

console.log(window.add);

Tương tự, nếu bạn khai báo một biến bên ngoài hàm bằng từ khóa var, JavaScript engine cũng thêm biến đó vào đối tượng chung:

var counter = 10;
console.log(window.counter); // 10

Nếu bạn có nhiều biến và hàm toàn cục, JavaScript engine sẽ chỉ giải phóng bộ nhớ được phân bổ cho chúng cho đến khi đối tượng toàn cục mất đi phạm vi của nó.

Kết quả là tập lệnh có thể sử dụng bộ nhớ không hiệu quả. Trên hết, việc có các biến và hàm toàn cục có thể sẽ gây ra xung đột tên.

Một cách để ngăn chặn các hàm và biến làm ô nhiễm đối tượng toàn cục là sử dụng các biểu thức hàm được gọi ngay lập tức (IIFE).

Trong JavaScript, bạn có thể có các biểu thức sau:

'This is a string';
(10+20);

Cú pháp này đúng mặc dù các biểu thức không có tác dụng. Một hàm cũng có thể được khai báo dưới dạng một biểu thức được gọi là biểu thức hàm:

let sum = function(a, b) {
    return a + b;
}

Trong cú pháp này, phần bên phải của toán tử gán( =) là một biểu thức hàm. Vì hàm là một biểu thức nên bạn có thể gói nó trong dấu ngoặc đơn:

let sum = (function(a, b) {
    return a + b;
});

Trong ví dụ này, biến sum được tham chiếu dưới dạng hàm ẩn danh (anonymous function) có thêm hai đối số. Ngoài ra, bạn có thể thực thi hàm ngay sau khi tạo:

let sum = (function(a,b){
    return a + b;
})(10, 20);
console.log(sum);  //30

Trong ví dụ trên , biến sum chứa kết quả của lệnh gọi hàm.

Biểu thức sau đây được gọi là biểu thức hàm được gọi ngay lập tức (IIFE) vì hàm được tạo dưới dạng biểu thức và được thực thi ngay lập tức:

(function(a,b){
        return a + b;
})(10,20);

Đây là cú pháp chung để xác định IIFE:

(function(){
    //...
})();

Lưu ý rằng bạn có thể sử dụng hàm mũi tên (arrow function) để xác định IIFE:

(() => {
    //...
})();

Bằng cách đặt các hàm và biến bên trong một biểu thức hàm được gọi ngay lập tức, bạn có thể tránh làm ô nhiễm chúng cho đối tượng toàn cục:

(function() {
    var counter = 0;

    function add(a, b) {
        return a + b;
    }

    console.log(add(10,20)); // 30
}())

Đặt tên IIFE

IIFE có thể có tên. Tuy nhiên, nó không thể được gọi lại sau khi thực thi:

(function namedIIFE() {
    //...
})();

IIFE bắt đầu bằng dấu chấm phẩy (;)

Đôi khi, bạn có thể thấy IIFE bắt đầu bằng dấu chấm phẩy(;):

;(function() {
/* */
})();

Trong cú pháp này, dấu chấm phẩy được sử dụng để kết thúc câu lệnh trong trường hợp hai hoặc nhiều tệp JavaScript được ghép nối một cách mù quáng vào một tệp duy nhất.

Ví dụ: bạn có thể có hai tệp lib1.js và lib2.js sử dụng IIFE:

(function(){
    // ...
})()

(function(){
    // ...
})()

Nếu bạn sử dụng công cụ gộp mã để ghép mã từ cả hai tệp thành một tệp duy nhất mà không có dấu chấm phẩy ( ;) thì mã JavaScript được nối sẽ gây ra lỗi cú pháp.

Sử dụng IIFE

Giả sử bạn có một thư viện có tên là calculator.js với các chức năng sau:

function add(a, b) {
    return a + b;
}

function mutiply(a, b) {
    return a * b;
}

Và bạn tải calculator.js vào một tài liệu HTML.

Sau đó, bạn cũng muốn tải một thư viện JavaScript khác được gọi là app.jsvào cùng một tài liệu:

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>JavaScript IIFE</title>
</head>
<body>
  <script src="calculator.js"></script>
  <script src="app.js"></script>
</body>
</html>

Ngoài ra  app.js còn có hàm add():

function add() {
    return 'add';
}

Khi bạn sử dụng hàm add() trong tài liệu HTML, nó sẽ trả về chuỗi 'add' thay vì tổng của hai số:

let result = add(10, 20);
console.log(result); // 'add'

Điều này là do hàm add() trong phần app.js ghi đè hàm add() trong calculator.js

Để khắc phục điều này, bạn có thể áp dụng IIFE trong calculator.js như sau:

const calculator = (function () {
    function add(a, b) {
        return a + b;
    }

    function multiply(a, b) {
        return a * b;
    }
    return {
        add: add,
        multiply: multiply
    }
})();

IIFE trả về một đối tượng chứa các phương thức add và multiply tham chiếu đến các hàm add() và multiply(). Trong tài liệu HTML, bạn có thể sử dụng thư viện calculator.js như sau:

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>JavaScript IIFE</title>
</head>
<body>
  <script src="js/calculator.js"></script>
  <script src="js/app.js"></script>
  <script>
    let result = calculator.add(10, 20); // add in app.js
    console.log(result); // 30
    console.log(add()); // add in the app.js
  </script>
</body>
</html>

Hàm calculator.add() sẽ  gọi add() trong calculator.js trong khi lệnh gọi add() thứ hai tham chiếu đến hàm add() trong app.js.

jQuery & IIFE

Tài liệu HTML sau đây sử dụng thư viện jQuery:

<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>JavaScript IIFE - jQuery</title>
</head>
<body>
  <h1>jQuery Demo</h1>
  <script src="https://code.jquery.com/jquery-3.4.1.slim.js"
    integrity="sha256-BTlTdQO9/fascB1drekrDVkaKd9PkwBymMlHOiG+qLI=" crossorigin="anonymous"></script>
  <script>
    let counter = 1;
    $('h1').click(function () {
      $(this).text('jQuery Demo' + ' Clicked ' + counter++);
    });
  </script>
</body>
</html>

Khi dùng thư viện jQuery, bạn có thể truy cập nhiều hàm jQuery hữu ích thông qua đối tượng $ hoặc jQuery. Về cơ bản, jQuery sử dụng IIFE để thể hiện chức năng của nó.

Bằng cách này, jQuery chỉ cần sử dụng một biến toàn cục ($) để hiển thị rất nhiều hàm mà không làm ảnh hưởng đến đối tượng toàn cục.

Ví dụ sau minh họa cách thay đổi đối tượng jQuery $ thành _ bên trong IIFE:

 (function (_) {
      let counter = 1;
      _('h1').click(function () {
        _(this).text('jQuery Demo' + ' Clicked ' + counter++);
      });
  })(jQuery);

 

» Tiếp: JavaScript Private Fields
« Trước: Kế thừa trong JavaScript bằng cách sử dụng super và extends
Khóa học qua video:
Lập trình Python All Lập trình C# All SQL Server All Lập trình C All Java PHP HTML5-CSS3-JavaScript
Đăng ký Hội viên
Tất cả các video dành cho hội viên
Copied !!!