Thứ sáu, 23/10/2020 | 00:00 GMT+7

Hiểu các module và các câu lệnh nhập và xuất trong JavaScript

Trong những ngày đầu của Web, các trang web chủ yếu bao gồm HTMLCSS . Nếu bất kỳ JavaScript nào được tải vào một trang, nó thường ở dạng các đoạn mã nhỏ cung cấp các hiệu ứng và tương tác. Do đó, các chương trình JavaScript thường được viết hoàn toàn trong một file và được tải vào thẻ script . Một nhà phát triển có thể chia JavaScript thành nhiều file , nhưng tất cả các biến và hàm vẫn sẽ được thêm vào phạm vi global .

Nhưng khi các trang web phát triển với sự ra đời của các khung công tác như Angular , ReactVue , và với các công ty tạo ra các ứng dụng web nâng cao thay vì các ứng dụng máy tính để bàn, JavaScript hiện đóng một role quan trọng trong trình duyệt. Do đó, nhu cầu sử dụng mã của bên thứ ba cho các việc phổ biến hơn nhiều, để chia nhỏ mã thành các file module và tránh làm ô nhiễm không gian tên chung.

Đặc tả ECMAScript 2015 đã giới thiệu các module cho ngôn ngữ JavaScript, cho phép sử dụng các câu lệnh importexport . Trong hướng dẫn này, bạn sẽ tìm hiểu module JavaScript là gì và cách sử dụng importexport để tổ chức mã của bạn.

Lập trình module

Trước khi khái niệm module xuất hiện trong JavaScript, khi một nhà phát triển muốn tổ chức mã của họ thành các phân đoạn, họ sẽ tạo nhiều file và liên kết đến chúng dưới dạng các tập lệnh riêng biệt. Để chứng minh điều này, hãy tạo một index.html và hai file JavaScript, functions.jsscript.js .

Tệp index.html sẽ hiển thị tổng, hiệu, tích và thương của hai số và liên kết đến hai file JavaScript trong thẻ script . Mở index.html trong editor và thêm mã sau:

index.html
<!DOCTYPE html> <html lang="en">   <head>     <meta charset="utf-8" />     <meta name="viewport" content="width=device-width, initial-scale=1.0" />      <title>JavaScript Modules</title>   </head>    <body>     <h1>Answers</h1>     <h2><strong id="x"></strong> and <strong id="y"></strong></h2>      <h3>Addition</h3>     <p id="addition"></p>      <h3>Subtraction</h3>     <p id="subtraction"></p>      <h3>Multiplication</h3>     <p id="multiplication"></p>      <h3>Division</h3>     <p id="division"></p>      <script src="functions.js"></script>     <script src="script.js"></script>   </body> </html> 

HTML này sẽ hiển thị giá trị của các biến xy trong tiêu đề h2 và giá trị của các phép toán trên các biến đó trong p phần tử sau. Thuộc tính id của các phần tử được đặt cho thao tác DOM , điều này sẽ xảy ra trong file script.js ; file này cũng sẽ đặt các giá trị của xy . Để biết thêm thông tin về HTML, hãy xem loạt bài Cách tạo trang web bằng HTML của ta .

Tệp functions.js sẽ chứa các hàm toán học sẽ được sử dụng trong tập lệnh thứ hai. Mở file functions.js và thêm thông tin sau:

functions.js
function sum(x, y) {   return x + y }  function difference(x, y) {   return x - y }  function product(x, y) {   return x * y }  function quotient(x, y) {   return x / y } 

Cuối cùng, file script.js sẽ xác định giá trị của xy , áp dụng các hàm cho chúng và hiển thị kết quả:

script.js
 const x = 10 const y = 5  document.getElementById('x').textContent = x document.getElementById('y').textContent = y  document.getElementById('addition').textContent = sum(x, y) document.getElementById('subtraction').textContent = difference(x, y) document.getElementById('multiplication').textContent = product(x, y) document.getElementById('division').textContent = quotient(x, y) 

Sau khi cài đặt và lưu các file này, bạn có thể mở index.html trong trình duyệt để hiển thị trang web với tất cả kết quả:

Hiển thị HTML với các giá trị 10 và 5 và kết quả của các hoạt động functions.js.

Đối với các trang web có một vài tập lệnh nhỏ, đây là một cách hiệu quả để phân chia mã. Tuy nhiên, có một số vấn đề liên quan đến cách tiếp cận này, bao gồm:

  • Gây ô nhiễm không gian tên chung : Tất cả các biến bạn đã tạo trong tập lệnh của bạn - sum , difference , v.v. - hiện tồn tại trên đối tượng window . Nếu bạn đã cố gắng sử dụng một biến khác được gọi là sum trong một file khác, sẽ rất khó để biết giá trị nào sẽ được sử dụng tại bất kỳ thời điểm nào trong các tập lệnh, vì tất cả chúng sẽ sử dụng cùng một biến window.sum . Cách duy nhất một biến có thể là riêng tư là đặt nó trong một phạm vi hàm. Thậm chí có thể có xung đột giữa một id trong DOM có tên là xvar x .
  • Quản lý phụ thuộc : Các tập lệnh sẽ phải được tải theo thứ tự từ trên xuống dưới đảm bảo có các biến chính xác. Việc lưu các tập lệnh dưới dạng các file khác nhau tạo ra ảo giác về sự tách biệt, nhưng về cơ bản nó giống như việc có một <script> nội tuyến duy nhất trong trang trình duyệt.

Trước khi ES6 thêm các module root vào ngôn ngữ JavaScript, cộng đồng đã cố gắng đưa ra một số giải pháp. Các giải pháp đầu tiên được viết bằng JavaScript vani, chẳng hạn như viết tất cả mã trong các đối tượng hoặc ngay lập tức gọi các biểu thức hàm (IIFE) và đặt chúng trên một đối tượng duy nhất trong không gian tên chung. Đây là một cải tiến đối với phương pháp tiếp cận nhiều tập lệnh, nhưng vẫn có những vấn đề tương tự khi đặt ít nhất một đối tượng vào không gian tên chung và không làm cho vấn đề chia sẻ mã nhất quán giữa các bên thứ ba trở nên dễ dàng hơn.

Sau đó, một số giải pháp module đã xuất hiện: CommonJS , một cách tiếp cận đồng bộ đã được triển khai trong Node.js , Định nghĩa module không đồng bộ (AMD) , là một cách tiếp cận không đồng bộ và Định nghĩa module chung (UMD) , được dự định là một cách tiếp cận đã hỗ trợ cả hai kiểu trước đó.

Sự ra đời của các giải pháp này đã giúp các nhà phát triển dễ dàng chia sẻ và sử dụng lại mã dưới dạng các gói , module có thể được phân phối và chia sẻ, chẳng hạn như các mã được tìm thấy trên npm . Tuy nhiên, vì có nhiều giải pháp và không có giải pháp nào có nguồn root từ JavaScript, nên các công cụ như Babel , Webpack hoặc Browserify phải được triển khai để sử dụng các module trong trình duyệt.

Do có nhiều vấn đề với cách tiếp cận nhiều file và sự phức tạp của các giải pháp được đề xuất, các nhà phát triển đã quan tâm đến việc đưa cách tiếp cận lập trình module vào ngôn ngữ JavaScript. Do đó, ECMAScript 2015 hỗ trợ việc sử dụng các module JavaScript.

Mô-đun là một gói mã hoạt động như một giao diện để cung cấp chức năng cho các module khác sử dụng, cũng như có thể dựa vào chức năng của các module khác. Mô-đun xuất để cung cấp mã và nhập để sử dụng mã khác. Các module rất hữu ích vì chúng cho phép các nhà phát triển sử dụng lại mã, chúng cung cấp một giao diện ổn định, nhất quán mà nhiều nhà phát triển có thể sử dụng và chúng không làm ô nhiễm không gian tên global .

Các module (đôi khi được gọi là module ECMAScript hoặc Mô-đun ES) hiện có sẵn trong JavaScript và trong phần còn lại của hướng dẫn này, bạn sẽ khám phá cách sử dụng và triển khai chúng trong mã của bạn .

Mô-đun JavaScript root

Các module trong JavaScript sử dụng các từ khóa importexport :

  • import : Dùng để đọc mã được xuất từ module khác.
  • export : Dùng để cung cấp mã cho các phân hệ khác.

Để trình bày cách sử dụng, hãy cập nhật file functions.js của bạn thành một module và xuất các chức năng. Bạn sẽ thêm export vào trước mỗi chức năng, điều này sẽ làm cho chúng có sẵn cho bất kỳ module nào khác.

Thêm mã được đánh dấu sau vào file của bạn:

functions.js
export function sum(x, y) {   return x + y }  export function difference(x, y) {   return x - y }  export function product(x, y) {   return x * y }  export function quotient(x, y) {   return x / y } 

Bây giờ, trong script.js , bạn sẽ sử dụng import để truy xuất mã từ module functions.js ở đầu file .

Lưu ý : import phải luôn ở đầu file trước bất kỳ mã nào khác và cũng cần phải bao gồm đường dẫn tương đối ( ./ trong trường hợp này).

Thêm mã được đánh dấu sau vào script.js :

script.js
 import { sum, difference, product, quotient } from './functions.js'  const x = 10 const y = 5  document.getElementById('x').textContent = x document.getElementById('y').textContent = y  document.getElementById('addition').textContent = sum(x, y) document.getElementById('subtraction').textContent = difference(x, y) document.getElementById('multiplication').textContent = product(x, y) document.getElementById('division').textContent = quotient(x, y) 

Lưu ý các hàm riêng lẻ được nhập bằng cách đặt tên chúng trong dấu ngoặc nhọn.

Để đảm bảo mã này được tải dưới dạng module và không phải là tập lệnh thông thường, hãy thêm type="module" vào các thẻ script trong index.html . Bất kỳ mã nào sử dụng import hoặc export đều phải sử dụng thuộc tính này:

index.html
... <script type="module" src="functions.js"></script> <script type="module" src="script.js"></script> 

Đến đây, bạn có thể reload trang với các bản cập nhật và trang web bây giờ sẽ sử dụng các module . Hỗ trợ trình duyệt là rất cao, nhưng caniuse có sẵn để kiểm tra mà các trình duyệt hỗ trợ nó. Lưu ý nếu bạn đang xem file dưới dạng liên kết trực tiếp đến file local , bạn sẽ gặp phải lỗi này:

Output
Access to script at 'file:///Users/your_file_path/script.js' from origin 'null' has been blocked by CORS policy: Cross-origin requests are only supported for protocol schemes: http, data, chrome, chrome-extension, chrome-untrusted, https.

Do chính sách CORS , Mô-đun phải được sử dụng trong môi trường server mà bạn có thể cài đặt local với server http hoặc trên internet với nhà cung cấp dịch vụ lưu trữ.

Mô-đun khác với các tập lệnh thông thường theo một số cách:

  • Mô-đun không thêm bất cứ thứ gì vào phạm vi global ( window ).
  • Các module luôn ở chế độ nghiêm ngặt .
  • Việc tải cùng một module hai lần trong cùng một file sẽ không có hiệu lực, vì các module chỉ được thực thi một lần.
  • Mô-đun yêu cầu một môi trường server .

Các module vẫn thường được sử dụng cùng với các gói như Webpack để tăng cường hỗ trợ trình duyệt và các tính năng bổ sung, nhưng chúng cũng có sẵn để sử dụng trực tiếp trong các trình duyệt.

Tiếp theo, bạn sẽ khám phá thêm một số cách mà cú pháp importexport được dùng .

Xuất khẩu được đặt tên

Như đã trình bày trước đó, sử dụng cú pháp export sẽ cho phép bạn nhập từng giá trị đã được xuất theo tên của chúng. Ví dụ: lấy version đơn giản này của functions.js :

functions.js
export function sum() {} export function difference() {} 

Điều này sẽ cho phép bạn nhập sumdifference theo tên bằng cách sử dụng dấu ngoặc nhọn:

script.js
import { sum, difference } from './functions.js' 

Cũng có thể sử dụng alias để đổi tên hàm. Bạn có thể làm điều này để tránh xung đột đặt tên trong cùng một module . Trong ví dụ này, sum sẽ được đổi tên để adddifference sẽ được đổi tên để subtract .

script.js
import {   sum as add,   difference as subtract } from './functions.js'  add(1, 2) // 3 

Gọi add() ở đây sẽ mang lại kết quả của hàm sum() .

Sử dụng cú pháp * , bạn có thể nhập nội dung của toàn bộ module vào một đối tượng. Trong trường hợp này, sumdifference sẽ trở thành các phương thức trên đối tượng mathFunctions .

script.js
import * as mathFunctions from './functions.js'  mathFunctions.sum(1, 2) // 3 mathFunctions.difference(10, 3) // 7 

Tất cả các giá trị nguyên thủy, biểu thức và định nghĩa hàm , hàm không đồng bộ , lớp và lớp khởi tạo đều có thể được xuất, miễn là chúng có mã định danh:

// Primitive values export const number = 100 export const string = 'string' export const undef = undefined export const empty = null export const obj = { name: 'Homer' } export const array = ['Bart', 'Lisa', 'Maggie']  // Function expression export const sum = (x, y) => x + y  // Function definition export function difference(x, y) {   return x - y }  // Asynchronous function export async function getBooks() {}  // Class export class Book {   constructor(name, author) {     this.name = name     this.author = author   } }  // Instantiated class export const book = new Book('Lord of the Rings', 'J. R. R. Tolkien') 

Tất cả các xuất này có thể được nhập thành công. Loại xuất khác mà bạn sẽ khám phá trong phần tiếp theo được gọi là xuất mặc định.

Xuất mặc định

Trong các ví dụ trước, bạn đã xuất nhiều bản xuất được đặt tên và nhập chúng riêng lẻ hoặc dưới dạng một đối tượng với mỗi lần xuất dưới dạng một phương thức trên đối tượng. Mô-đun cũng có thể chứa một bản xuất mặc định, sử dụng từ khóa default . Một bản xuất mặc định sẽ không được nhập bằng dấu ngoặc nhọn, nhưng sẽ được nhập trực tiếp vào số nhận dạng được đặt tên.

Ví dụ: lấy nội dung sau cho file functions.js :

functions.js
export default function sum(x, y) {   return x + y } 

Trong file script.js , bạn có thể nhập hàm mặc định dưới dạng sum với như sau:

script.js
import sum from './functions.js'  sum(1, 2) // 3 

Điều này có thể nguy hiểm, vì không có hạn chế nào về những gì bạn có thể đặt tên cho bản xuất mặc định trong quá trình nhập. Trong ví dụ này, hàm mặc định được nhập dưới dạng difference mặc dù nó thực sự là hàm sum :

script.js
import difference from './functions.js'  difference(1, 2) // 3 

Vì lý do này, nó thường được ưu tiên sử dụng hàng xuất khẩu có tên. Không giống như các bản xuất được đặt tên, các bản xuất mặc định không yêu cầu số nhận dạng — một giá trị nguyên thủy của chính nó hoặc hàm ẩn danh được dùng làm bản xuất mặc định. Sau đây là một ví dụ về một đối tượng được sử dụng làm xuất mặc định:

functions.js
export default {   name: 'Lord of the Rings',   author: 'J. R. R. Tolkien', } 

Bạn có thể nhập cái này dưới dạng book với những thứ sau:

script.js
import book from './functions.js' 

Tương tự, ví dụ sau minh họa xuất một hàm mũi tên ẩn danh làm xuất mặc định:

functions.js
export default () => 'This function is anonymous' 

Điều này có thể được nhập bằng script.js sau:

script.js
import anonymousFunction from './functions.js' 

Xuất khẩu được đặt tên và xuất khẩu mặc định được dùng song song với nhau, như trong module này xuất khẩu hai giá trị được đặt tên và một giá trị mặc định:

functions.js
export const length = 10 export const width = 5  export default function perimeter(x, y) {   return 2 * (x + y) } 

Bạn có thể nhập các biến này và hàm mặc định như sau:

script.js
import calculatePerimeter, { length, width } from './functions.js'  calculatePerimeter(length, width) // 30 

Bây giờ giá trị mặc định và giá trị được đặt tên đều có sẵn cho tập lệnh.

Kết luận

Thực tiễn thiết kế lập trình module cho phép bạn tách mã thành các thành phần riêng lẻ có thể giúp mã của bạn có thể tái sử dụng và nhất quán, đồng thời bảo vệ không gian tên chung. Một giao diện module có thể được triển khai bằng JavaScript root với các từ khóa importexport .

Trong bài viết này, bạn đã tìm hiểu về lịch sử của các module trong JavaScript, cách tách các file JavaScript thành nhiều tập lệnh cấp cao nhất, cách cập nhật các file đó bằng cách sử dụng phương pháp module và cú pháp importexport cho các file export được đặt tên và xuất mặc định.

Để tìm hiểu thêm về module trong JavaScript, hãy đọc Mô-đun trên Mạng nhà phát triển Mozilla. Nếu bạn muốn khám phá các module trong Node.js, hãy thử hướng dẫn Cách tạo Mô-đun Node.js của ta .


Tags:

Các tin trước

Xây dựng Đồng hồ đếm ngược trong JavaScript 2020-10-21
Giới thiệu về Thị giác máy tính trong JavaScript sử dụng OpenCV.js 2020-10-20
Thêm, loại bỏ & chuyển đổi lớp với classList trong JavaScript 2020-10-13
Cách Chèn Javascript vào HTML bằng Thẻ script 2020-09-23
Mẫu thiết kế module trong JavaScript 2020-09-21
Mẫu thiết kế trình quan sát trong JavaScript 2020-09-21
Mẫu thiết kế Singleton trong JavaScript 2020-09-21
Mẫu thiết kế nguyên mẫu trong JavaScript 2020-09-21
Lời hứa của JavaScript dành cho người giả 2020-09-15
Sao chép các đối tượng trong JavaScript 2020-09-15