Trả về
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
2 nếu giá trị âm, currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
3 nếu giá trị là currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
3 và abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
7 nếu giá trị dương. Hữu ích cho phép nhânabbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
2toInt[val]. Con số #
"Chuyển đổi" giá trị thành số nguyên 32 bit. Hoạt động như
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
6 nếu currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
7 và currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
8 nếu currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
9QUAN TRỌNG. val sẽ bọc ở số/MIN_INT và số/MAX_INT
Được tạo ra bởi vì hầu hết mọi người không biết các hoạt động bitwise và cũng bởi vì tính năng này thường cần thiết
kiểm tra hoàn hảo
Ví dụ
abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
3toUInt[val]. Con số #
"Chuyển đổi" giá trị thành số nguyên không dấu 32 bit
Hoạt động như AS3#uint[]
QUAN TRỌNG. val sẽ bọc ở 2^32
Ví dụ
abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
4toUInt31[val]. Con số #
"Chuyển đổi" giá trị thành số nguyên không dấu 31 bit [vì 1 bit được sử dụng cho dấu]
Hữu ích vì tất cả các toán tử bitwise bên cạnh
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
10 coi các số là số nguyên có dấuQUAN TRỌNG. val sẽ bọc ở 2^31 và số âm sẽ được coi là
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
11Ví dụ
abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
5Để biết thêm ví dụ sử dụng, hãy kiểm tra thông số kỹ thuật bên trong thư mục
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
12. Bài kiểm tra đơn vị là tài liệu tốt nhất bạn có thể nhận được Tôi đang làm việc trên một trang xử lý thông tin địa chỉ IP, nhưng thực tế là các số nguyên được ký. Tôi đang sử dụng các toán tử bitwise để tăng tốc độ, nhưng bit thứ 64 [cờ đã ký/không dấu] đang làm hỏng nó
Có cách nào để buộc một số không được ký trong Javascript không?
Thử cái này
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
5Kết quả
1073741824
-2147483648
1
Giải pháp tốt nhất
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
6Toán tử
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
65 được định nghĩa là làm việc trên các số nguyên 32 bit đã ký [được chuyển đổi từ bộ lưu trữ Số gốc của float có độ chính xác kép]. Vì vậy, currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
66 phải dẫn đến một số âmToán tử JavaScript duy nhất hoạt động bằng cách sử dụng số nguyên 32 bit không dấu là
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
67. Bạn có thể khai thác điều này để chuyển đổi một số nguyên có dấu mà bạn đang làm việc với các toán tử bitwise khác thành một số nguyên không dấucurrencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
0Trong khi đó
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
1sẽ không hoạt động vì tất cả các hoạt động thay đổi chỉ sử dụng 5 bit dịch chuyển thấp nhất [trong JavaScript và các ngôn ngữ giống C khác].
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
68 bằng với currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
69, tức là. không thay đổiGiải pháp liên quan
Javascript – Cách đóng JavaScript hoạt động
Một đóng cửa là một cặp của
- Một chức năng, và
- Tham chiếu đến phạm vi bên ngoài của hàm đó [môi trường từ vựng]
Môi trường từ vựng là một phần của mọi ngữ cảnh thực thi [khung ngăn xếp] và là bản đồ giữa các mã định danh [nghĩa là. tên biến cục bộ] và giá trị
Mỗi chức năng trong JavaScript duy trì một tham chiếu đến môi trường từ vựng bên ngoài của nó. Tham chiếu này được sử dụng để định cấu hình ngữ cảnh thực thi được tạo khi một hàm được gọi. Tham chiếu này cho phép mã bên trong hàm "thấy" các biến được khai báo bên ngoài hàm, bất kể khi nào và ở đâu hàm được gọi
Nếu một chức năng được gọi bởi một chức năng, đến lượt nó được gọi bởi một chức năng khác, thì một chuỗi tham chiếu đến môi trường từ vựng bên ngoài được tạo. Chuỗi này được gọi là chuỗi phạm vi
Trong đoạn mã sau,
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
00 tạo thành một bao đóng với môi trường từ vựng của ngữ cảnh thực thi được tạo khi gọi ____201, đóng trên biến ____202currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
7Nói cách khác. trong JavaScript, các hàm mang tham chiếu đến một "hộp trạng thái" riêng tư mà chỉ chúng [và bất kỳ hàm nào khác được khai báo trong cùng một môi trường từ vựng] mới có quyền truy cập. Hộp trạng thái này là vô hình đối với người gọi hàm, cung cấp một cơ chế tuyệt vời để ẩn và đóng gói dữ liệu
Và hãy nhớ. các hàm trong JavaScript có thể được truyền xung quanh giống như các biến [hàm hạng nhất], nghĩa là các cặp chức năng và trạng thái này có thể được truyền xung quanh chương trình của bạn. tương tự như cách bạn có thể truyền một thể hiện của một lớp xung quanh trong C++
Nếu JavaScript không có bao đóng, thì sẽ phải chuyển nhiều trạng thái hơn giữa các hàm một cách rõ ràng, làm cho danh sách tham số dài hơn và mã ồn hơn
Vì vậy, nếu bạn muốn một chức năng luôn có quyền truy cập vào một phần trạng thái riêng tư, bạn có thể sử dụng bao đóng
và chúng ta thường muốn liên kết trạng thái với một chức năng. Ví dụ, trong Java hoặc C++, khi bạn thêm một biến thể hiện riêng và một phương thức vào một lớp, bạn đang liên kết trạng thái với chức năng
Trong C và hầu hết các ngôn ngữ phổ biến khác, sau khi một hàm trả về, tất cả các biến cục bộ không còn truy cập được nữa do khung ngăn xếp bị hủy. Trong JavaScript, nếu bạn khai báo một hàm bên trong một hàm khác, thì các biến cục bộ của hàm bên ngoài vẫn có thể truy cập được sau khi trở về từ hàm đó. Theo cách này, trong đoạn mã trên,
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
02 vẫn có sẵn cho đối tượng hàm currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
00, sau khi nó được trả về từ currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
01Công dụng của Đóng cửa
Các bao đóng hữu ích bất cứ khi nào bạn cần một trạng thái riêng tư được liên kết với một chức năng. Đây là một kịch bản rất phổ biến - và hãy nhớ. JavaScript không có cú pháp lớp cho đến năm 2015 và nó vẫn không có cú pháp trường riêng. Đóng cửa đáp ứng nhu cầu này
Biến thể hiện cá nhân
Trong đoạn mã sau, hàm
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
06 đóng trên các chi tiết của ô tôabbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
2Lập trình chức năng
Trong đoạn mã sau, hàm
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
00 đóng trên cả currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
08 và currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
09abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
6Lập trình hướng sự kiện
Trong đoạn mã sau, hàm
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
10 đóng trên biến currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
11abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
9abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
0mô đun hóa
Trong ví dụ sau, tất cả các chi tiết triển khai được ẩn bên trong một biểu thức hàm được thực thi ngay lập tức. Các chức năng
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
12 và currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
06 đóng trên trạng thái tư nhân và các chức năng họ cần để hoàn thành công việc của mình. Việc đóng cửa đã cho phép chúng tôi mô đun hóa và đóng gói mã của chúng tôiabbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
3ví dụ
ví dụ 1
Ví dụ này cho thấy các biến cục bộ không được sao chép trong bao đóng. việc đóng cửa duy trì một tham chiếu đến chính các biến ban đầu. Như thể khung ngăn xếp vẫn tồn tại trong bộ nhớ ngay cả sau khi chức năng bên ngoài thoát
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
60ví dụ 2
Trong đoạn mã sau, ba phương thức
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
14, currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
15 và currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
16 đều đóng trên cùng một môi trường từ vựngVà mỗi khi
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
17 được gọi, một bối cảnh thực thi mới [khung ngăn xếp] được tạo và một biến hoàn toàn mới currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
18 và một bộ hàm mới [currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
14, v.v. ] được tạo, đóng trên biến mới nàycurrencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
61ví dụ 3
Nếu bạn đang sử dụng các biến được khai báo bằng cách sử dụng
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
70, hãy cẩn thận để biết bạn đang đóng biến nào. Các biến được khai báo bằng cách sử dụng currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
70 được nâng lên. Đây là một vấn đề ít hơn nhiều trong JavaScript hiện đại do sự ra đời của currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
72 và currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
73Trong đoạn mã sau, mỗi lần lặp lại, một hàm mới
currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
00 được tạo, hàm này sẽ đóng trên currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
75. Nhưng vì currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
76 được nâng lên bên ngoài vòng lặp, tất cả các hàm bên trong này đóng trên cùng một biến, nghĩa là giá trị cuối cùng của currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
75 [3] được in ba lầncurrencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
62điểm cuối cùng
- Bất cứ khi nào một hàm được khai báo trong JavaScript, bao đóng được tạo
- Trả về một
78 từ bên trong một hàm khác là ví dụ cổ điển về đóng, bởi vì trạng thái bên trong hàm bên ngoài hoàn toàn khả dụng đối với hàm bên trong được trả về, ngay cả sau khi hàm bên ngoài đã hoàn tất thực thicurrencyFormat[1000]; // "1,000.00" currencyFormat[1000, 1]; // "1,000.0" currencyFormat[1000, 2, ',', '.']; // "1.000,00"
- Bất cứ khi nào bạn sử dụng
79 bên trong một chức năng, thì bao đóng được sử dụng. Văn bản mà bạncurrencyFormat[1000]; // "1,000.00" currencyFormat[1000, 1]; // "1,000.0" currencyFormat[1000, 2, ',', '.']; // "1.000,00"
20 có thể tham chiếu đến các biến cục bộ của hàm và ở chế độ không nghiêm ngặt, bạn thậm chí có thể tạo các biến cục bộ mới bằng cách sử dụngabbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
21abbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
- Khi bạn sử dụng
22 [Hàm tạo chức năng] bên trong một chức năng, nó sẽ không đóng trên môi trường từ vựng của nó. thay vào đó, nó đóng trên bối cảnh toàn cầu. Hàm mới không thể tham chiếu các biến cục bộ của hàm bên ngoàiabbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
- Một bao đóng trong JavaScript giống như giữ một tham chiếu [KHÔNG phải là một bản sao] đến phạm vi tại điểm khai báo hàm, từ đó giữ một tham chiếu đến phạm vi bên ngoài của nó, v.v., đến tận đối tượng chung ở đầu
- Một bao đóng được tạo khi một hàm được khai báo;
- Một tập hợp các biến cục bộ mới được tạo mỗi khi một hàm được gọi
liên kết
- Các thuộc tính riêng tư mô phỏng của Douglas Crockford và các phương thức riêng tư cho một đối tượng, sử dụng các bao đóng
- Một lời giải thích tuyệt vời về cách đóng cửa có thể gây rò rỉ bộ nhớ trong IE nếu bạn không cẩn thận
- Tài liệu MDN về Đóng JavaScript
Javascript – cách hiệu quả nhất để sao chép sâu một đối tượng trong JavaScript
Bản địa sâu nhân bản
Nó được gọi là "nhân bản có cấu trúc", hoạt động thử nghiệm trong Nút 11 trở lên và hy vọng sẽ xuất hiện trên các trình duyệt. Xem câu trả lời này để biết thêm chi tiết
Sao chép nhanh với mất dữ liệu - JSON. phân tích/xâu chuỗiNếu bạn không sử dụng các
abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
23, hàm, abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
24, abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
25, RegExps, Maps, Sets, Blobs, FileLists, ImageDatas, thưa thớt Arrays, Typed Arrays hoặc các loại phức tạp khác trong đối tượng của bạn, thì một lớp lót rất đơn giản để sao chép sâu một đối tượng làabbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
26currencyFormat[1000]; // "1,000.00"
currencyFormat[1000, 1]; // "1,000.0"
currencyFormat[1000, 2, ',', '.']; // "1.000,00"
63Xem câu trả lời của Corban để biết điểm chuẩn
Nhân bản đáng tin cậy bằng thư việnVì các đối tượng nhân bản không phải là tầm thường [các loại phức tạp, tham chiếu vòng tròn, chức năng, v.v. ], hầu hết các thư viện lớn đều cung cấp chức năng sao chép các đối tượng. Đừng phát minh lại bánh xe - nếu bạn đang sử dụng thư viện, hãy kiểm tra xem thư viện đó có chức năng nhân bản đối tượng không. Ví dụ,
- lodash -
27; . clonedeep và có lẽ là lựa chọn tốt nhất của bạn nếu bạn chưa sử dụng thư viện cung cấp chức năng nhân bản sâuabbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
- AngularJS -
28abbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
- jQuery -
29;abbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
- thư viện thôi -
61; . Tiện ích miễn phí cho mọi dịpabbreviate[543]; // "0.5K" abbreviate[543, 1]; // "0.5K" abbreviate[543, 2]; // "0.54K" abbreviate[543, 3]; // "0.543K"
Để hoàn thiện, lưu ý rằng ES6 cung cấp hai cơ chế sao chép nông.
abbreviate[543]; // "0.5K"
abbreviate[543, 1]; // "0.5K"
abbreviate[543, 2]; // "0.54K"
abbreviate[543, 3]; // "0.543K"
62 và cú pháp lây lan. sao chép các giá trị của tất cả các thuộc tính riêng có thể đếm được từ đối tượng này sang đối tượng khác. Ví dụ