Đối tượng là một túi chứa các giá trị có tên
Mảng nhóm các giá trị theo vị trí. Đối tượng (object) nhóm chúng theo tên. Khi thứ bạn mô tả có các phần được gắn nhãn, chẳng hạn một người dùng có tên và tuổi, một request có phương thức và URL, hãy dùng đối tượng.
Cách ngắn nhất để tạo một đối tượng là object literal:
Mỗi mục là một cặp key: value. Bên dưới, khóa là chuỗi (không cần dấu nháy nếu là định danh hợp lệ như name). Giá trị có thể là bất cứ thứ gì JavaScript biểu diễn được: số, chuỗi, boolean, mảng, hàm, thậm chí là đối tượng khác.
Dấu phẩy sau mục cuối cùng không gây lỗi. Phần lớn các nhóm phát triển giữ lại dấu phẩy này để diff gọn gàng khi thêm thuộc tính mới sau này.
Đọc và ghi thuộc tính
Có hai cách truy cập thuộc tính: dùng dấu chấm và dùng ngoặc vuông.
Dấu chấm gọn gàng hơn và là cách bạn dùng mặc định. Ngoặc vuông phát huy tác dụng khi khóa nằm trong một biến, hoặc khi khóa không phải là định danh hợp lệ:
Đọc một thuộc tính không tồn tại sẽ trả về undefined, không có lỗi nào:
Điều đó có thể âm thầm che giấu lỗi gõ sai. Nếu muốn chương trình báo lỗi rõ ràng, bạn cần tự kiểm tra.
Thêm và xóa thuộc tính
Đối tượng luôn sẵn sàng nhận thêm: bạn có thể thêm khóa bất cứ lúc nào:
Và xóa chúng bằng delete:
delete là một trong những toán tử bạn không dùng hằng ngày, nhưng khi cần bỏ hẳn một khóa (chứ không chỉ gán nó thành undefined), đây là công cụ đúng. Gán user.email = undefined vẫn giữ khóa đó: "email" in user vẫn sẽ là true.
Kiểm tra một thuộc tính có tồn tại không
Có ba cách, mỗi cách mang ý nghĩa hơi khác nhau:
inkiểm tra khóa có tồn tại không, kể cả khóa được kế thừa từ chuỗi prototype.Object.hasOwn(obj, key)chỉ kiểm tra các khóa của chính đối tượng. Hãy dùng cách này khi bạn muốn bỏ qua những thứ được kế thừa. Nó thay thế cho lời gọihasOwnPropertycũ.obj.key !== undefinedđúng trong phần lớn trường hợp nhưng cho kết quả sai khi một thuộc tính được gán rõ ràng làundefined.
Khi không chắc, hãy chọn Object.hasOwn: nó làm đúng điều mà bạn nhiều khả năng đang muốn.
Phương thức: hàm nằm trên đối tượng
Một thuộc tính có giá trị là hàm được gọi là phương thức (method). Có một cú pháp ngắn để định nghĩa phương thức bên trong object literal:
this bên trong một phương thức trỏ tới đối tượng mà phương thức được gọi trên đó, ở đây là user. Nhờ vậy greet biết cần dùng tên của ai.
Một điều cần lưu ý: arrow function không có this của riêng nó, nên đây là lựa chọn sai cho những phương thức cần đọc các thuộc tính khác của đối tượng:
Hãy dùng cú pháp phương thức rút gọn (greet() { ... }) cho bất cứ thứ gì đụng tới this. Arrow function rất hợp làm callback, nhưng không hợp làm phương thức của đối tượng.
Đối tượng lồng nhau
Giá trị cũng có thể là đối tượng, lồng bao nhiêu cấp cũng được:
Đọc một thuộc tính lồng bên trong bằng cách nối chuỗi truy cập: user.address.city. Vấn đề là: nếu bất kỳ bước nào trên đường đi là null hoặc undefined, bạn sẽ gặp TypeError:
console.log(user.profile.city);
// TypeError: Cannot read properties of undefined (reading 'city')
Optional chaining (user.profile?.city) là cách khắc phục hiện đại: nó trả về undefined thay vì ném lỗi khi một mắt xích bị thiếu. Có một trang riêng về chủ đề này ở phần sau của chương.
Duyệt qua một đối tượng
Khi cần đi qua mọi khóa trong một đối tượng, nhóm Object.keys, Object.values và Object.entries là thứ bạn cần:
Object.entries hữu ích nhất: nó cho bạn cả khóa lẫn giá trị cùng lúc, và kết hợp rất tốt với phân rã mảng.
Ngoài ra còn có vòng lặp for...in, nhưng nó duyệt cả các thuộc tính được kế thừa, thường không phải điều bạn muốn:
for (const key in scores) {
console.log(key); // works, but includes inherited keys
}
Hãy ưu tiên Object.keys / Object.entries, trừ khi bạn thực sự muốn lấy cả thuộc tính được kế thừa.
Cú pháp rút gọn nên biết
Hai kiểu cú pháp tiện lợi mà bạn sẽ thấy ở khắp nơi:
Cách viết thuộc tính rút gọn có tác dụng khi tên biến trùng với khóa. Khóa tính toán (dạng [expr]) cho phép bạn tạo tên thuộc tính một cách động, rất có ích khi viết hàm cập nhật một trường theo tên.
So sánh đối tượng theo tham chiếu
Điều này khiến gần như ai cũng vấp ít nhất một lần:
=== với đối tượng kiểm tra xem hai vế có trỏ tới cùng một đối tượng trong bộ nhớ không, chứ không kiểm tra nội dung có giống nhau không. Hai đối tượng có thuộc tính y hệt nhau vẫn là hai đối tượng khác nhau.
Để trả lời câu hỏi "hai đối tượng này có cùng cấu trúc không?", bạn cần so sánh từng trường thủ công hoặc dùng một hàm hỗ trợ so sánh sâu. JSON.stringify(a) === JSON.stringify(b) là cách kiểm tra nhanh, dùng được với dữ liệu thuần nhưng sẽ hỏng với bất cứ thứ gì có hàm, undefined hoặc tham chiếu vòng.
Đối tượng vẫn thay đổi được dù khai báo bằng const
const khóa biến vào một giá trị. Nó không đóng băng đối tượng mà giá trị đó trỏ tới:
Nếu thực sự muốn đối tượng bất biến, Object.freeze(user) sẽ chặn mọi thay đổi tiếp theo (chỉ ở cấp nông: đối tượng lồng bên trong vẫn thay đổi được). Trong thực tế, phần lớn code dựa vào quy ước hơn là Object.freeze: hãy hiểu đối tượng const là "đừng gán lại biến này" và xử lý tính bất biến ở cấp thiết kế.
Tiếp theo: mảng
Đối tượng và mảng là hai khối xây dựng tạo nên mọi thứ khác. Đối tượng xử lý dữ liệu có nhãn; mảng xử lý dữ liệu có thứ tự. Bài tiếp theo: mảng trong JavaScript hoạt động thế nào, và một số phương thức (push, slice, map và những phương thức tương tự) gánh phần lớn công việc.
Câu hỏi thường gặp
Làm sao tạo một đối tượng trong JavaScript?
Cách thông dụng là dùng object literal: const user = { name: 'Ada', age: 30 }. Cặp ngoặc nhọn chứa các cặp khóa và giá trị, ngăn cách bằng dấu phẩy. Khóa là chuỗi (không cần dấu nháy nếu là định danh hợp lệ), còn giá trị có thể là bất cứ thứ gì: số, chuỗi, mảng, hàm, đối tượng khác.
Dấu chấm và ngoặc vuông khác nhau thế nào?
user.name và user['name'] làm cùng một việc khi khóa là một định danh đã biết. Dùng ngoặc vuông khi khóa nằm trong một biến (user[key]), chứa ký tự mà cách viết dấu chấm không đọc được (khoảng trắng, dấu gạch nối) hoặc bắt đầu bằng chữ số. Với mọi trường hợp còn lại, dấu chấm gọn gàng hơn.
Làm sao kiểm tra một thuộc tính có tồn tại trên đối tượng không?
Dùng 'name' in user để kiểm tra khóa, kể cả khóa được kế thừa. Dùng Object.hasOwn(user, 'name') để chỉ kiểm tra thuộc tính của chính đối tượng: đây là cách hiện đại thay cho hasOwnProperty. Kiểm tra user.name !== undefined cũng được, nhưng sẽ cho kết quả sai nếu thuộc tính được gán rõ ràng là undefined.
Làm sao duyệt qua một đối tượng trong JavaScript?
for (const key in obj) duyệt qua các khóa, kể cả khóa được kế thừa. Thông dụng hơn là dùng Object.keys(obj), Object.values(obj) hoặc Object.entries(obj) với vòng lặp for...of hay .forEach(). Object.entries rất tiện khi bạn cần cả khóa lẫn giá trị trong cùng một lần lặp.