Menu

Destructuring JavaScript: đối tượng, mảng, mặc định và rest

Destructuring trong JavaScript hoạt động thế nào: lấy giá trị từ đối tượng và mảng, đổi tên, giá trị mặc định, mẫu lồng nhau và tham số hàm.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Destructuring là so khớp theo hình dạng

Destructuring cho phép bạn viết một mẫu phản chiếu hình dạng của một đối tượng hay mảng, rồi lấy giá trị ra theo tên hoặc vị trí chỉ trong một dòng, giống kiểu Python. Thay vì lấy từng thuộc tính một, bạn mô tả thứ mình muốn và JavaScript trao nó cho bạn.

Hai dạng:

Dấu ngoặc nhọn so khớp đối tượng theo tên thuộc tính. Dấu ngoặc vuông so khớp mảng theo vị trí. Trong cả hai trường hợp, các biến ở vế trái là khai báo mới: bạn không truy cập vào giá trị theo chỉ số, mà đang mô tả một mẫu để tách các phần ra.

Trước khi có destructuring, cùng đoạn code đó trông như thế này:

const name = user.name;
const age = user.age;
const first = scores[0];
const second = scores[1];

Không phải thảm họa, nhưng lặp đi lặp lại khi bạn phải làm vậy ở mọi hàm.

Destructuring đối tượng dùng tên thuộc tính

Với đối tượng, các tên bên trong dấu ngoặc nhọn phải trùng với tên thuộc tính của nguồn. Thứ tự không quan trọng, vì JavaScript tra theo khóa.

Nếu đối tượng không có thuộc tính đó, biến sẽ là undefined, không có lỗi nào:

Hành vi undefined âm thầm này rất đáng nhớ, kẻo lúc 2 giờ sáng bạn ngồi gãi đầu vì gõ nhầm một tên thuộc tính.

Đổi tên biến

Đôi khi tên thuộc tính trên đối tượng không phải là tên bạn muốn đặt cho biến: có thể nó trùng với thứ khác trong phạm vi, hoặc API dùng quy ước đặt tên bạn không thích. Dùng original: newName để gán sang tên khác:

Hãy đọc là "lấy thuộc tính id, gọi nó là userId." Tên thuộc tính đứng trước, tên biến mới đứng sau dấu hai chấm. Nó trông như chú thích kiểu trong các ngôn ngữ khác, nhưng không phải.

Giá trị mặc định

Nếu một thuộc tính có thể bị thiếu, hãy đặt giá trị mặc định ngay trong mẫu:

Giá trị mặc định chỉ được dùng khi giá trị là undefined. null, 0 và "" đều giữ nguyên:

Điểm này hay làm người ta vấp. Nếu muốn null cũng kích hoạt giá trị mặc định, bạn cần kiểm tra rõ ràng, hoặc dùng toán tử nullish coalescing ?? sau khi destructuring.

Bạn có thể kết hợp giá trị mặc định với đổi tên:

Destructuring mảng dùng vị trí

Mảng không có tên, chỉ có chỉ số, nên mẫu so khớp theo vị trí. Dùng dấu phẩy để bỏ qua những phần tử bạn không cần:

Hai dấu phẩy đầu có nghĩa là "bỏ qua chỉ số 0 và 1." Nhìn hơi rối mắt, nhưng tiện khi bạn chỉ cần một giá trị ở giữa.

Destructuring mảng tỏa sáng với các hàm trả về tuple:

Mẫu này xuất hiện khắp nơi trong React hook: const [count, setCount] = useState(0).

Mẫu rest: gom phần còn lại

Bên trong một mẫu destructuring, ...name gom mọi thứ chưa được so khớp theo tên hoặc vị trí vào một biến mới.

Với mảng, rest lấy phần đuôi:

Với đối tượng, rest lấy mọi thuộc tính chưa được nêu tên:

Đây là cách viết quen thuộc để bỏ một trường khỏi đối tượng mà không thay đổi đối tượng gốc: rest là một đối tượng mới chứa mọi thứ trừ id.

Rest phải là phần tử cuối cùng trong mẫu. const [...init, last] là lỗi cú pháp.

Destructuring lồng nhau

Các mẫu có thể lồng nhau. Nếu một thuộc tính cũng là đối tượng hay mảng, bạn có thể destructuring vào bên trong nó ngay trong cùng một biểu thức:

Rất mạnh, nhưng hãy dùng có chừng mực. Ba tầng ngoặc nhọn lồng nhau bắt đầu giống một câu đố. Khi đến mức đó, hãy tách các giá trị trung gian ra biến riêng: dễ đọc quan trọng hơn khéo léo.

Một cái bẫy: data: { user } trong mẫu không tạo ra biến tên data. Nó là chỉ dẫn "đi vào bên trong data và tiếp tục destructuring." Nếu bạn cần chính data, hãy thêm nó riêng:

Destructuring tham số hàm

Nơi bạn destructuring nhiều nhất là danh sách tham số của hàm. Nó biến kiểu "truyền một đối tượng tùy chọn" thành một chữ ký hàm tự giải thích:

Ở chỗ gọi, nó trông như mọi lời gọi truyền đối tượng tùy chọn khác. Bên trong hàm, bạn có các biến được đặt tên sẵn kèm giá trị mặc định, không còn phải viết đi viết lại options.name || "default".

Một góc cạnh cần cẩn thận: gọi hàm này mà không truyền đối số sẽ gây lỗi, vì bạn không thể destructuring undefined. Hãy đặt giá trị mặc định cho cả tham số:

= {} sau mẫu có nghĩa là "nếu không có đối số nào được truyền vào, coi như đó là một đối tượng rỗng." Sau đó các giá trị mặc định bên trong sẽ lo phần còn lại.

Hoán đổi và gán lại

Destructuring cũng dùng được với các biến đã khai báo, nhưng bạn cần đặt mẫu đối tượng trong dấu ngoặc tròn (nếu không, JavaScript sẽ hiểu { là bắt đầu một khối lệnh):

Hoán đổi bằng mảng rất gọn. Gán lại bằng đối tượng hiếm gặp hơn, nhưng mẹo dấu ngoặc tròn đáng ghi nhớ để dùng khi cần.

Một ví dụ thực tế

Kết hợp gần hết những gì ở trên: một hàm xử lý response từ API:

Mỗi tầng destructuring đều có giá trị mặc định, nên hàm vẫn chạy được với dữ liệu thiếu mà không cần cả đống câu lệnh if bảo vệ. Đó mới là lợi ích thật: chữ ký hàm vừa chính xác vừa dễ dãi.

Tiếp theo: object spread

Destructuring lấy giá trị ra khỏi đối tượng. Spread đặt chúng vào: sao chép, gộp và ghi đè. Hai thứ này luôn đi cùng nhau trong JavaScript hiện đại, và spread là bài tiếp theo.

Câu hỏi thường gặp

Destructuring trong JavaScript là gì?

Destructuring là cú pháp để lấy giá trị ra khỏi đối tượng hoặc mảng và gán chúng vào biến chỉ trong một bước. const { name } = user; lấy thuộc tính name từ user. const [first, second] = list; lấy hai phần tử đầu tiên của list. Đó là một mẫu đặt ở vế trái của =, phản chiếu hình dạng của giá trị ở vế phải.

Làm sao đổi tên biến khi destructuring?

Dùng cú pháp { original: newName }. const { name: username } = user; lấy thuộc tính name từ user và gán vào biến cục bộ tên username. Cú pháp này dễ bị đọc nhầm thành chú thích kiểu: hãy nhớ tên thuộc tính đứng trước, tên biến mới đứng sau dấu hai chấm.

Có thể đặt giá trị mặc định khi destructuring không?

Có. const { timeout = 5000 } = options; dùng 5000 nếu options.timeout là undefined. Giá trị mặc định chỉ áp dụng cho undefined: null, 0 và '' vẫn giữ nguyên. Bạn có thể kết hợp giá trị mặc định với đổi tên: const { t: timeout = 5000 } = options;.

Destructuring và spread khác nhau thế nào?

Trông giống nhau nhưng chúng làm hai việc ngược nhau. Destructuring (const { a, b } = obj) lấy giá trị ra khỏi một cấu trúc. Spread (const copy = { ...obj }) đặt giá trị vào một cấu trúc mới. Dấu ... bên trong một mẫu destructuring là mẫu rest: nó gom phần còn lại vào một biến mới.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu