Lúc mới học JS, mình chỉ biết mỗi một kiểu cơ bản nhất là:
1 2 3 4 | <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> |
1 2 3 4 5 6 7 8 9 10 | <span class="token doctype"><!DOCTYPE html></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>html</span> <span class="token attr-name">lang</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>en<span class="token punctuation">"</span></span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span> <span class="token attr-name">src</span><span class="token attr-value"><span class="token punctuation">=</span><span class="token punctuation">"</span>calculator.js<span class="token punctuation">"</span></span><span class="token punctuation">></span></span><span class="token script" /><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>script</span><span class="token punctuation">></span></span><span class="token script"><span class="token language-javascript"> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">4</span><span class="token punctuation">,</span> <span class="token number">6</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token comment">// 10</span> </span></span><span class="token tag"><span class="token tag"><span class="token punctuation"></</span>script</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>body</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>html</span><span class="token punctuation">></span></span> |
Đú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:
1 2 3 4 | <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
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:
1 2 3 4 5 | <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// code</span> <span class="token punctuation">}</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
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ụ:
1 2 3 4 5 6 7 | <span class="token keyword">var</span> text <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">var</span> privateText <span class="token operator">=</span> <span class="token string">"Text không thể truy cập!"</span><span class="token punctuation">;</span> <span class="token keyword">var</span> publicText <span class="token operator">=</span> <span class="token string">"Text được phép truy cập!"</span> <span class="token keyword">return</span> publicText<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>text<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Text được phép truy cập!</span> |
Một ví dụ phức tạp hơn:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | <span class="token keyword">var</span> calculator <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">subtract</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">divide</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">/</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> <span class="token punctuation">{</span> add<span class="token operator">:</span> add<span class="token punctuation">,</span> subtract<span class="token operator">:</span> subtract<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 0</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">divide</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Xảy ra lỗi vì hàm "divide" không được xuất ra ngoài</span> |
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:
1 2 3 4 5 6 7 8 9 10 11 12 | <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">subtract</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> <span class="token punctuation">{</span> <span class="token comment">// những gì cần xuất ra ngoài được đặt trong này</span> add<span class="token operator">:</span> add<span class="token punctuation">,</span> subtract<span class="token operator">:</span> subtract<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
1 2 3 4 5 | <span class="token comment">// dùng "require" để load module "calculator.js" (phần đuôi .js có thể được lược bỏ)</span> <span class="token keyword">var</span> calculator <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./calculator"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 0</span> |
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:
1 2 | <span class="token function">define</span><span class="token punctuation">(</span>id<span class="token operator">?</span><span class="token punctuation">,</span> dependencies<span class="token operator">?</span><span class="token punctuation">,</span> factory<span class="token punctuation">)</span><span class="token punctuation">;</span> |
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:
1 2 3 4 5 6 7 8 9 10 | <span class="token comment">// module với tên "calculator" được khai báo là một object chứa 2 hàm.</span> <span class="token function">define</span><span class="token punctuation">(</span><span class="token string">"calculator"</span><span class="token punctuation">,</span> <span class="token punctuation">{</span> <span class="token function-variable function">add</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">subtract</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 | <span class="token comment">// dùng hàm "require" để load module "calculator"</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"calculator"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">calculator</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 4</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 0</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">calculator</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> define <span class="token operator">===</span> <span class="token string">'function'</span> <span class="token operator">&&</span> define<span class="token punctuation">.</span>amd<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// xuất bằng AMD</span> <span class="token function">define</span><span class="token punctuation">(</span><span class="token string">"calculator"</span><span class="token punctuation">,</span> calculator<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> module <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&&</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token comment">// xuất bằng CJS</span> module<span class="token punctuation">.</span>exports <span class="token operator">=</span> calculator<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span> <span class="token comment">// xuất bằng browser global</span> window<span class="token punctuation">.</span>calculator <span class="token operator">=</span> calculator<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">(</span><span class="token punctuation">{</span> <span class="token function-variable function">add</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token function-variable function">subtract</span><span class="token operator">:</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <span class="token comment">// load bằng AMD</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"calculator"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">calculator</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// load bằng CJS</span> <span class="token keyword">var</span> calculator <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./calculator"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// nếu trong browser thì có thể truy xuất trực tiếp</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
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:
1 2 3 4 | <span class="token keyword">import</span> React <span class="token keyword">from</span> <span class="token string">"react"</span><span class="token punctuation">;</span> <span class="token keyword">import</span> App <span class="token keyword">from</span> <span class="token string">"./App"</span><span class="token punctuation">;</span> <span class="token function">render</span><span class="token punctuation">(</span><span class="token operator"><</span>App <span class="token operator">/</span><span class="token operator">></span><span class="token punctuation">,</span> document<span class="token punctuation">.</span><span class="token function">getElementById</span><span class="token punctuation">(</span><span class="token string">"root"</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Đú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:
1 2 3 4 5 6 7 8 9 10 11 | <span class="token keyword">function</span> <span class="token function">add</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">+</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">function</span> <span class="token function">subtract</span><span class="token punctuation">(</span><span class="token parameter">a<span class="token punctuation">,</span> b</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">return</span> a <span class="token operator">-</span> b<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> add<span class="token punctuation">,</span> subtract<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">;</span> |
1 2 3 4 | <span class="token keyword">import</span> calculator <span class="token keyword">from</span> <span class="token string">"./calculator"</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> console<span class="token punctuation">.</span><span class="token function">log</span><span class="token punctuation">(</span>calculator<span class="token punctuation">.</span><span class="token function">subtract</span><span class="token punctuation">(</span><span class="token number">2</span><span class="token punctuation">,</span> <span class="token number">2</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
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:1234<span class="token keyword">var</span> moduleName <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">function</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// return something to export</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
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:123456<span class="token comment">// export</span>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> factory<span class="token punctuation">;</span><span class="token comment">// import</span><span class="token keyword">var</span> alias <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">"./moduleName"</span><span class="token punctuation">)</span>
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:12345678<span class="token comment">// export</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">"moduleName"</span><span class="token punctuation">,</span> <span class="token punctuation">[</span><span class="token string">"dependency1"</span><span class="token punctuation">,</span> <span class="token string">"dependency2"</span><span class="token punctuation">,</span> <span class="token operator">...</span><span class="token punctuation">]</span><span class="token punctuation">,</span> factory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token comment">// import</span><span class="token function">require</span><span class="token punctuation">(</span><span class="token punctuation">[</span><span class="token string">"moduleName"</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token keyword">function</span><span class="token punctuation">(</span><span class="token parameter">alias</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token comment">// code</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
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:12345678910<span class="token punctuation">(</span><span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token parameter">factory</span><span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> define <span class="token operator">===</span> <span class="token string">'function'</span> <span class="token operator">&&</span> define<span class="token punctuation">.</span>amd<span class="token punctuation">)</span> <span class="token punctuation">{</span><span class="token function">define</span><span class="token punctuation">(</span><span class="token string">"moduleName"</span><span class="token punctuation">,</span> factory<span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">typeof</span> module <span class="token operator">===</span> <span class="token string">'object'</span> <span class="token operator">&&</span> module<span class="token punctuation">.</span>exports<span class="token punctuation">)</span> <span class="token punctuation">{</span>module<span class="token punctuation">.</span>exports <span class="token operator">=</span> factory<span class="token punctuation">;</span><span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{</span>window<span class="token punctuation">[</span><span class="token string">"moduleName"</span><span class="token punctuation">]</span> <span class="token operator">=</span> factory<span class="token punctuation">;</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">(</span>factory<span class="token punctuation">)</span><span class="token punctuation">;</span>
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:12345678910111213141516<span class="token comment">// default exports</span><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">function</span> <span class="token function">fnA</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token keyword">export</span> <span class="token keyword">default</span> <span class="token keyword">class</span> <span class="token class-name">classB</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token comment">// named exports</span><span class="token keyword">export</span> <span class="token keyword">function</span> <span class="token function">fnA</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token punctuation">}</span><span class="token keyword">export</span> <span class="token keyword">var</span> varB <span class="token operator">=</span> <span class="token string">""</span><span class="token punctuation">;</span><span class="token comment">// default import</span><span class="token keyword">import</span> alias <span class="token keyword">from</span> <span class="token string">"moduleName"</span><span class="token punctuation">;</span><span class="token comment">// named imports</span><span class="token keyword">import</span> <span class="token operator">*</span> <span class="token keyword">as</span> alias <span class="token keyword">from</span> <span class="token string">"moduleName"</span><span class="token punctuation">;</span><span class="token keyword">import</span> <span class="token punctuation">{</span> fnA<span class="token punctuation">,</span> varB <span class="token punctuation">}</span> <span class="token keyword">from</span> <span class="token string">"moduleName"</span><span class="token punctuation">;</span>
Tham khảo
- FreeCodeCamp – JavaScript Modules: A Beginner’s Guide
- Dev.to – What are CJS, AMD, UMD, and ESM in JavaScript
- Wikipedia – Asynchronous module definition
- Github – AMD Specification
- Github – UMD Pattern
- ExploringJS – ES6 Modules