Table of Contents
Table of Contents
Giới thiệu
Tôi có một mảng (Object) với rất nhiều keys, tôi có thể thấy chúng bằng cách dùng console.log.
Tôi có Object.keys().
Ahhh, tôi có … dừng khoảng chừng là 5 giây. Tại sao đó lại là một mảng rỗng thay vì một mảng chứa nhiều chuỗi?
Bạn đã từng sử dụng console.log
với dữ liệu được trả về bởi phương thức Object.keys
, và bạn thấy một mảng trống thay vì một mảng đầy đủ các chuỗi như mong đợi chưa? Nghe có vẻ lạ, phải không? Nếu bạn đã gặp vấn đề này và vẫn đang tìm kiếm một giải pháp, có thể bạn đã đến đúng nơi rồi đó.
Trong bài đăng này, tôi sẽ giới thiệu tới các bạn về khái niệm Enumerability (Tính liệt kê) và Ownership (Tính sở hữu) của mảng (Object) trong JavaScript. Ngoài ra, tôi cũng sẽ cung cấp ví dụ để làm rõ những khái niệm này và hy vọng bạn sẽ có thể hiểu chúng một cách rõ ràng.
Vậy thì tính liệt kê và tính sở hữu của mảng trong JavaScript là gì?
Chia nhỏ câu hỏi ra, chúng ta sẽ thấy có 2 từ khoá chính: tính liệt kê and tính sở hữu và cùng nhau đi tìm kiểu từng khái niệm.
Enumerability
Tính liệt kê xác định xem một thuộc tính có được bao gồm trong một số phương thức JavaScript cụ thể hay không, những phương thức này dùng để duyệt hoặc truy vấn các thuộc tính của đối tượng. Tính liệt kê được kiểm soát bởi cờ liệt kê nội tại (the internal enumerable flag) của một thuộc tính.
Tính liệt kê bao gồm 2 loại: Enumerable Properties (Thuộc tính có thể liệt kê) và Non-enumerable Properties (Thuộc tính không thể liệt kê)
- Enumerable Properties (Thuộc tính có thể liệt kê)
- Mặc định, tất cả các thuộc tính của một đối tượng là có thể liệt kê được.
- Một thuộc tính có thể liệt kê thì có thể được truy xuất và lặp thông qua vòng lặp
for…in
hoặc những phương thức như làObject.keys()
,Object.values()
, vàObject.entries()
.
- Ở đây, chúng ta dùng phương thức
Object.keys()
để lấy các thuộc tính có thể liệt kê của đối tượng. Phương thức này trả về một mảng bao gồm các khoá (keys) của các thuộc tính có thể liệt kê, như ở đây thì là‘name’
và‘age’
.- Non-enumerable Properties (Thuộc tính không thể liệt kê)
- Các thuộc tính không thể liệt kê là các thuộc tính của đối tượng không được ghi nhận khi lặp qua các thuộc tính của một đối tượng bằng cách dùng vòng lặp hoặc phương thức như là
Object.keys()
. Điều đó có nghĩa rằng, thuộc tính không thể liệt kê có cờ liệt kê nội tại (the internal enumerable flag) được đặt là false - Các thuộc tính được định nghĩa bằng
Object.defineProperty()
hoặc một số đối tượng tích hợp sẵn cụ thể thì không thể liệt kê.
- Các thuộc tính không thể liệt kê là các thuộc tính của đối tượng không được ghi nhận khi lặp qua các thuộc tính của một đối tượng bằng cách dùng vòng lặp hoặc phương thức như là
- Non-enumerable Properties (Thuộc tính không thể liệt kê)
- Trong đoạn mã này, chúng ta định nghĩa một thuộc tính không thể liệt kê name cho đối tượng bằng cách sử dụng Object.defineProperty(). Khi chúng ta sử dụng phương thức Object.keys() để lất toàn bộ thuộc tính có thể liệt kê, phương thức sẽ trả về một mảng rỗng bởi vì thuộc tính không thể liệt kê sẽ không được chọn.
Ownership
Tính sở hữu của thuộc tính được xác định bởi việc thuộc tính đó có thuộc về đối tượng trực tiếp hay không, và không phải là của chuỗi prototype của nó.
Tính sở hữu bao gồm 2 loại: Own properties (Thuộc tính sở hữu) và Inherited properties (Thuộc tính kế thừa).
- Own properties (Thuộc tính sở hữu)
- Các thuộc tính sỡ hữu được định nghĩa trực tiếp trên đối tượng, không được kế thừa từ các prototype.
- Để xác định một thuộc tính có là thuộc tính sở hữu hay không, bạn có thể sử dụng phương thức
Object.hasOwnProperty()
hoặcObject.hasOwn()
. Những phương thức này cho phép bạn kiểm tra liệu một thuộc tính cụ thể có thể được định nghĩa trực tiếp trên đối tượng hay là nó được kế thừa từ chuỗi prototype.
- Ở đây, chúng ta sử dụng
Object.hasOwnProperty()
vàObject.hasOwn()
để kiểm tra xem thuộc tínhname
có thuộc trực tiếp vào đối tượng hay không. Cả hai phương thức đều trả vềtrue
vì thuộc tính được thiết lập trực tiếp trên đối tượng. - Inherited properties (Thuộc tính kế thừa)
- Các thuộc tính được kế thừa là những thuộc tính được lấy từ chuỗi prototype của đối tượng.
- Những thuộc tính này khác biệt so với các thuộc tính sở hữu (own properties) vì chúng không được định nghĩa trực tiếp trên đối tượng mà có thể truy cập thông qua quyền giao thừa từ prototype.
- Chúng ta tạo một đối tượng và liên kết nó với một đối tượng cha bằng cách sử dụng Object.setPrototypeOf(). Đối tượng nhận một thuộc tính ‘name’ từ đối tượng cha của nó. Khi chúng ta kiểm tra với obj.hasOwnProperty() hoặc Object.hasOwn(), nó trả về ‘false’ vì ‘name’ được kế thừa, không được thiết lập trực tiếp. Tuy nhiên, chúng ta vẫn có thể lấy giá trị thuộc tính bằng cách sử dụng obj.name vì nó tra cứu chuỗi prototype.
Example
Bây giờ, hãy giải quyết một vấn đề cụ thể:
Hãy tưởng tượng bạn đang tạo một thẻ div
, kiểm tra các styles của nó bằng console.log
và sử dụng Object.keys
để tạo ra một mảng chưa các styles đó. Tuy nhiên, mọi thứ đều ổn trên Chrome, nhưng trên Safari, nó trả về một mảng rỗng.
Vấn đề ở đây là gì? Trên Chrome, CSSStyleDeclaration được xem là cả Thuộc tính có thể liệt kê và Thuộc tính sở hữu. Điều này có nghĩa là chúng ta có thể sử dụng Object.keys
để lấy tất cả các khóa (keys) của CSSStyleDeclaration. Tuy nhiên, Safari tạo ra nó theo cách khác – nó coi CSSStyleDeclaration là Thuộc tính có thể liệt kê và Thuộc tính được kế thừa. Thật không may, điều này gây ra vấn đề và chúng ta không thể sử dụng Object.keys
trên Safari.
Bây giờ, làm thế nào chúng ta có thể khắc phục vấn đề này và làm cho nó hoạt động mượt mà trên cả Chrome và Safari?
Để hiểu rõ hơn về Tính liệt kê và quyền sở hữu của các thuộc tính trong JavaScript, hãy kiểm tra thông tin chi tiết được cung cấp trong tài liệu của Mozilla Developer Network. Họ cung cấp một bảng tổng hợp chi tiết liệt kê tất cả các phương thức có sẵn để sử dụng.
Sau khi xem xét cẩn thận, nó rõ ràng để nói rằng cách hiệu quả nhất để có được một mảng với tất cả các khóa (keys) của CSSStyleDeclaration, đồng thời đảm bảo tính tương thích với cả Chrome và Safari, là sử dụng phương thức for…in. Hãy áp dụng kiến thức này vào thực tế và xem nó có phù hợp hay không.
Kết quả đây rồi, cùng một kết quả trên cả Chrome và Safari.
Tóm lại, việc nắm vững tính liệt kê và tính kế thừa của đối tượng trong JavaScript là vô cùng quan trọng. Tôi hy vọng bạn có thể tìm thấy nhiều thông tin hữu ích trong bài đăng này để có thể áp dụng trong quá trình làm việc với đối tượng trong JavaScript.
Happy Coding!!!
Tài liệu tham khảo:
Enumerability and ownership of properties – MDN Web Docs
Ron