- vừa được xem lúc

JS Modules - Bao nhiêu kiểu khai báo, làm sao nhớ hết?

0 0 11

Người đăng: Khang

Theo Viblo Asia

Lúc mới học JS, mình chỉ biết mỗi một kiểu cơ bản nhất là:

function add(a, b) { return a + b;
}
<!DOCTYPE html>
<html lang="en">
<body> <script src="calculator.js"></script> <script> console.log(add(4, 6)) // 10 </script>
</body>
</html>

Đúng vậy, khi cần tách code, cứ nhóm tất cả các hàm liên quan với nhau vào một file, khi cần lôi các hàm ra dùng thì nhúng file này vào trang HTML bằng thẻ <script>, quá ez luôn. Nhưng khi bắt đầu ra lăn lộn giang hồ + sự tiến hóa không ngừng của công nghệ, mình gặp phải nhiều cú pháp ngoài hình tinh để tách code, cũng bị xoắn não và mệt mỏi lắm mới có thể nắm bắt được. Nên trong bài viết này, mình xin tổng hợp và chia sẻ các kiểu khai báo module phục vụ việc tách code và cách phân biệt từng kiểu để dễ nhớ.

Bài viết sẽ không nói về khái niệm "Module là gì?" hay "Sao phải sử dụng module?", bởi vì đã có nhiều bài viết trả lời những câu hỏi này, và mình nghĩ chắc nhiều bạn cũng như mình, chả thể nuốt nổi ba cái lý thuyết nếu không ứng dụng thực tế để thật sự hiểu và tự trả lời cho mấy câu hỏi trên.

Các kiểu khai báo

1. IIFE

Viết tắt của Immediately Invoked Function Expression, hiểu đơn giản là "tạo hàm, gọi liền". Đây là kỹ thuật sơ khai nhất được các bậc tiền nhân phát minh và vẫn đang được sử dụng rộng rãi cho các bộ compiler/bundler như Babel, Webpack, Rollup... nhằm chuyển các cú pháp khai báo module hiện đại về code JavaScript thuần (< ES6). Nếu các bạn đọc source của jQuery, thư viện từng thống trị một thời, thì sẽ thấy nhiều đoạn cũng sử dụng IIFE. Cú pháp có dạng như sau:

(function() { // code
})();

Nhìn có vẻ hack não bởi cả đống ngoặc, nhưng nếu viết dạng tương đương dưới đây chắc bạn dễ nhìn hơn:

function add() { // code
}
add();

Như các bạn thấy, hàm add() sau khi tạo liền được gọi, để hiểu rõ nữa thì các bạn chịu khó search bài viết khác nhé, mình quay lại với cách khai báo module. IIFE giúp ẩn các code được xử lý bên trong và chỉ xuất ra những gì mà chúng ta muốn. Ví dụ:

var text = (function() { var privateText = "Text không thể truy cập!"; var publicText = "Text được phép truy cập!" return publicText;
})();
console.log(text); // Text được phép truy cập!

Một ví dụ phức tạp hơn:

var calculator = (function() { function add(a, b) { return a + b; } function subtract(a, b) { return a - b; } function divide(a, b) { return a / b; } return { add: add, subtract: subtract, };
})();
console.log(calculator.add(2, 2)); // 4
console.log(calculator.subtract(2, 2)); // 0
console.log(calculator.divide(2, 2)); // Xảy ra lỗi vì hàm "divide" không được xuất ra ngoài

2. CJS

Viết tắt của CommonJS. Dân chơi hệ back-end, đặc biệt là những ai hay sử dụng NodeJS chắc hẳn không lạ gì với cái tên này. Xuất hiện lần đầu với tên gọi ServerJS thay vì CommonJS như bây giờ, nghe tên là biết ngay mục tiêu của dự án là nhắm vào một hệ sinh thái module dành cho JavaScript phía back-end, CommonJS đến nay vẫn đang gắn bó thân thiết với một số nền tảng phổ biến như NodeJS, MongoDB... Ví dụ cách khai báo module bằng CJS:

function add(a, b) { return a + b;
}
function subtract(a, b) { return a - b;
}
module.exports = { // những gì cần xuất ra ngoài được đặt trong này add: add, subtract: subtract,
};
// dùng "require" để load module "calculator.js" (phần đuôi .js có thể được lược bỏ)
var calculator = require("./calculator");
console.log(calculator.add(2, 2)); // 4
console.log(calculator.subtract(2, 2)); // 0

3. AMD

Viết tắt của Asynchronous Module Definition. Như tên gọi, nhiệm vụ của AMD là quy định kiểu khai báo cho module và load bất đồng bộ các dependency của module đó nếu có. Do đó, trái với CJS, AMD sinh ra để dành cho dân chơi hệ front-end. Cú pháp đầy đủ có dạng như sau:

define(id?, dependencies?, factory);

Các đối số:

  • id (string): Tên định danh cho module. Không bắt buộc, nhưng cần khi khai báo module.
  • dependencies (array): mảng chứa tên các module khác cần được load. Không bắt buộc nếu không có dependency.
  • factory (object/function): object hoặc function để khởi tạo cho module.

Cùng xem ví dụ sau để dễ hình dung hơn:

// module với tên "calculator" được khai báo là một object chứa 2 hàm.
define("calculator", { add: function(a, b) { return a + b; }, subtract: function(a, b) { return a - b; },
});
// dùng hàm "require" để load module "calculator"
require(["calculator"], function(calculator) { console.log(calculator.add(2, 2)); // 4 console.log(calculator.subtract(2, 2)); // 0
});

4. UMD

Viết tắt của Universal Module Definition. UMD có một sứ mệnh là mang back-end và front-end đến gần nhau hơn, bằng việc hỗ trợ nhiều trường hợp để tương thích cho cả hai, bao gồm cả browser global. Vì lý do này mà code của UMD tương đối phức tạp, chúng ta cùng xem ví dụ khai báo một module bằng UMD:

(function (calculator) { if (typeof define === 'function' && define.amd) { // xuất bằng AMD define("calculator", calculator); } else if (typeof module === 'object' && module.exports) { // xuất bằng CJS module.exports = calculator; } else { // xuất bằng browser global window.calculator = calculator; }
}({ add: function(a, b) { return a + b; }, subtract: function(a, b) { return a - b; },
}));
// load bằng AMD
require(["calculator"], function (calculator) { console.log(calculator.add(2, 2)); console.log(calculator.subtract(2, 2));
}); // load bằng CJS
var calculator = require("./calculator");
console.log(calculator.add(2, 2));
console.log(calculator.subtract(2, 2)); // nếu trong browser thì có thể truy xuất trực tiếp
console.log(calculator.add(2, 2));
console.log(calculator.subtract(2, 2));

Qua ví dụ thì các bạn có thể thấy, UMD tập hợp các trường hợp khai báo module khác nhau, giúp module "calculator" có thể được sử dụng cho tất cả môi trường từ trước (front-end) ra sau (back-end).

5. ESM

Viết tắt của ECMAScript Modules. Còn gọi là ES6 Modules, ESM chính là giải pháp cho một tương lai tươi sáng của hệ sinh thái module bằng việc chuẩn hóa cú pháp khai báo module, hoạt động từ trước ra sau, và được hỗ trợ bởi chính JavaScript mà không cần thông qua bất kì một compiler hay loader nào (trừ việc phải polyfill từ ES6 về các phiên bản ES trước cho các trình duyệt cũ). Chắc hẳn nhiều bạn đã quá quen với đoạn code sau:

import React from "react";
import App from "./App";
render(<App />, document.getElementById("root"));

Đúng vậy, đoạn code được sử dụng trong index.js của một React App, sử dụng ESM để load các module. Giờ chúng ta cùng xem một ví dụ khác tương đương các ví dụ trên bằng ESM:

function add(a, b) { return a + b;
}
function subtract(a, b) { return a - b;
}
export default { add, subtract,
};
import calculator from "./calculator";
console.log(calculator.add(2, 2));
console.log(calculator.subtract(2, 2));

Ví dụ trên sử dụng default export để xuất tất cả những gì cần xuất trong duy nhất một object, ngoài cách này, ESM còn hỗ trợ named export cho phép xuất nhiều thứ cùng lúc. Để biết thêm, các bạn search bài viết khác hoặc tham khảo các liên kết bên dưới nhé.

Tổng kết

Đọc đến đây chắc cũng có bạn còn mông lung, vẫn chưa rõ tại sao lại có quá nhiều kiểu khai báo module trong JavaScript, cũng như làm sao để nhớ hết cả đống kiến thức trên. Mình xin tóm tắt lại những cái quan trọng để mọi người cùng nhớ:

1. IIFE (Immediately Invoked Function Expression)

  • Kỹ thuật khai báo module cho JS thuần (ES5 trở về trước)
  • Cú pháp:
    var moduleName = (function() { // return something to export
    })();
    

2. CJS (CommonJS)

  • Cú pháp khai báo module được sử dụng dưới back-end (phổ biến nhất là NodeJS)
  • Các module được import một cách đồng bộ
  • Cú pháp:
    // export
    module.exports = factory; // import
    var alias = require("./moduleName")
    

3. AMD (Asynchronous Module Definition)

  • Cú pháp khai báo module được sử dụng trên front-end (thông qua bộ loader phổ biến là RequireJS)
  • Các module được import một cách bất đồng bộ
  • Cú pháp:
    // export
    define("moduleName", ["dependency1", "dependency2", ...], factory); // import
    require(["moduleName"], function(alias) { // code
    });
    

4. UMD (Universal Module Definition)

  • Kỹ thuật kết hợp nhiều cú pháp khai báo module
  • Tương thích với front-end lẫn back-end
  • Cú pháp tương đối phức tạp
  • Cú pháp:
    (function (factory) { if (typeof define === 'function' && define.amd) { define("moduleName", factory); } else if (typeof module === 'object' && module.exports) { module.exports = factory; } else { window["moduleName"] = factory; }
    })(factory);
    

5. ESM (ECMAScript Modules)

  • Cú pháp khai báo module được chuẩn hóa, hỗ trợ trực tiếp bởi JS thuần (ES6 trở về sau)
  • Tương thích với front-end lẫn back-end
  • Cú pháp đơn giản nhưng đa dạng, linh động hơn
  • Cú pháp:
    // default exports
    export default { }
    export default function fnA() { }
    export default class classB { } // named exports
    export function fnA() { }
    export var varB = ""; // default import
    import alias from "moduleName"; // named imports
    import * as alias from "moduleName";
    import { fnA, varB } from "moduleName";
    

Tham khảo


@khangnd
Github Linkedin Dev.to Fandom

Bình luận

Bài viết tương tự

- vừa được xem lúc

Giới thiệu Typescript - Sự khác nhau giữa Typescript và Javascript

Typescript là gì. TypeScript là một ngôn ngữ giúp cung cấp quy mô lớn hơn so với JavaScript.

0 0 502

- vừa được xem lúc

Bạn đã biết các tips này khi làm việc với chuỗi trong JavaScript chưa ?

Hi xin chào các bạn, tiếp tục chuỗi chủ đề về cái thằng JavaScript này, hôm nay mình sẽ giới thiệu cho các bạn một số thủ thuật hay ho khi làm việc với chuỗi trong JavaScript có thể bạn đã hoặc chưa từng dùng. Cụ thể như nào thì hãy cùng mình tìm hiểu trong bài viết này nhé (go).

0 0 415

- vừa được xem lúc

Một số phương thức với object trong Javascript

Trong Javascript có hỗ trợ các loại dữ liệu cơ bản là giống với hầu hết những ngôn ngữ lập trình khác. Bài viết này mình sẽ giới thiệu về Object và một số phương thức thường dùng với nó.

0 0 137

- vừa được xem lúc

Tìm hiểu về thư viện axios

Giới thiệu. Axios là gì? Axios là một thư viện HTTP Client dựa trên Promise.

0 0 119

- vừa được xem lúc

Imports và Exports trong JavaScript ES6

. Giới thiệu. ES6 cung cấp cho chúng ta import (nhập), export (xuất) các functions, biến từ module này sang module khác và sử dụng nó trong các file khác.

0 0 94

- vừa được xem lúc

Bài toán đọc số thành chữ (phần 2) - Hoàn chỉnh chương trình dưới 100 dòng code

Tiếp tục bài viết còn dang dở ở phần trước Phân tích bài toán đọc số thành chữ (phần 1) - Phân tích đề và những mảnh ghép đầu tiên. Bạn nào chưa đọc thì có thể xem ở link trên trước nhé.

0 0 230