Buổi 20 chính là "trùm cuối" của hệ thống dữ liệu trong JavaScript. Khác với Buổi 19 chỉ là cái vỏ bọc cấu trúc, Buổi 20 ném cho bạn bộ công cụ để thao túng, khóa chặt và nhân bản các Object đó ở cấp độ sâu nhất (phục vụ trực tiếp cho việc xử lý cấu hình Web, SDK và tracking data).
1. QUYỀN LỰC KIỂM SOÁT (Bảo vệ dữ liệu không bị hacker/script khác ghi đè)
Khi viết cấu hình cho web (ví dụ tỷ lệ giảm giá, phí ship), nếu để Object hớ hênh, một đoạn script lỗi nào đó có thể vô tình sửa mất số liệu. F8 ép bạn học 3 cấp độ khóa:
Cấp độ 1: Object.freeze(obj) — Đóng băng tuyệt đối
- Cơ học: Biến Object thành một tảng băng. Cấm thêm, cấm xóa, cấm sửa.
- Ứng dụng (Bài 2 - Yêu cầu 1): Dùng để tạo các Object cấu hình mặc định (Config). Nếu ai cố tình gán đè
config.mucPhuPhi = 0.8, máy sẽ âm thầm bỏ qua (silently fail) hoặc chửi thẳng mặt báo lỗi (ở strict mode). Kiểm tra bằngObject.isFrozen(obj).
Cấp độ 2: Object.seal(obj) — Niêm phong
- Cơ học: Đóng kín cái hộp. Cấm nhét thêm đồ mới vào, cấm vứt đồ cũ đi. NHƯNG ĐƯỢC PHÉP sửa giá trị của đồ đã có sẵn.
- Ứng dụng (Bài 1 - Yêu cầu 8): Khi bạn có 1 Object nhân viên, không cho phép thêm thuộc tính
nguoiYeu, nhưng cho phép cập nhậtluong. Kiểm tra bằngObject.isSealed(obj).
Cấp độ 3: Object.defineProperty() — Kiểm soát từng cọng lông
Cơ học: Can thiệp vào cấp độ DNA (Descriptor) của một cái nhãn (key) bất kỳ. Mỗi cái nhãn đều ẩn chứa 3 công tắc ngầm:
writable: Có cho sửa giá trị không? (true/false)enumerable: Có cho phép vòng lặpfor...inhoặcObject.keys()nhìn thấy nó không? (true/false)configurable: Có cho phép xóa (delete) cái nhãn này không? (true/false)Ứng dụng (Bài 2 - Yêu cầu 4): Tạo ra một cái
idđịnh danh ẩn danh. Gánenumerable: falseđể khi Hacker dùng vòng lặp quét Object của bạn, cáiidđó sẽ tàng hình. Gánwritable: falseđể không ai hack đổi id được.
2. CỖ MÁY NHÀO NẶN DỮ LIỆU (Data Transformation)
Đây là mớ vũ khí giúp bạn sống sót khi làm việc với các mảng dữ liệu (Array of Objects) khổng lồ đổ về từ API.
Object.assign(target, source1, source2)— Sáp nhập:Cơ học: Gom tất cả thuộc tính của source đắp vào target. Thằng nào đi sau sẽ đè lên thằng đi trước nếu trùng tên.
- Ứng dụng (Bài 2 - Yêu cầu 5): Mẹo tạo bản sao không làm chết gốc (Shallow copy):
const merged = Object.assign({}, objA, objB). Tạo ra cái hộp{}mới tinh rồi mới đắp đồ vào. Object.groupBy(iterable, callback)— Chia lô phân loại:- Cơ học: Ném cho nó 1 mảng các Object. Nó tự động tạo ra các thư mục con dựa trên điều kiện bạn chỉ định.
- Ứng dụng (Bài 1 - Yêu cầu 9): Phân nhóm 1000 người dùng theo từng phòng ban chỉ bằng 1 dòng code. Trả về:
{ IT: [...], Marketing: [...] }. Object.fromEntries()— Lật ngược thế cờ:- Cơ học: Biến 1 mảng chứa các mảng con
[[key, value], [key, value]]thành một Object hoàn chỉnh. - Ứng dụng (Bài 1 - Yêu cầu 10): Dùng để dựng "Từ điển tra cứu nhanh". Thay vì phải dùng
.find()quét cả mảng để tìm mã số, bạn ép nó thành Object rồi tra bằngobj["A002"]tốc độ chạy dưới RAM nhanh gấp ngàn lần.
3. GETTER VÀ SETTER (Kẻ Gác Cổng)
Không phải thuộc tính nào cũng có sẵn. Đôi khi nó phải tự tính toán lúc được gọi ra.
get(Getter): Đóng giả làm một thuộc tính, nhưng thực chất bên trong là một hàm ngầm chạy tính toán.- Ứng dụng (Bài 2 - Yêu cầu 2): Gọi
instance.total, máy sẽ tự động kích hoạt hàmget total()để cộng dồn giá tiền của các món đồ, thay vì phải gọi hàminstance.getTotal(). set(Setter): Trạm kiểm duyệt dữ liệu đầu vào.- Ứng dụng:
set discountPercent(value). Khi ai đó gáninstance.discountPercent = 150, setter sẽ nhảy ra chặn họng kiểm traif (value > 100) throw new Error("Lỗi!"). Nếu hợp lệ nó mới cho ghi vào RAM.
4. GỠ RỐI TỪ KHÓA this (Bind, Call, Apply)
Đây là nguyên nhân gây ra 90% các vụ tai nạn "Sập Tracking" khi gắn JavaScript vào WordPress.
- Căn bệnh "Mất trí nhớ": Khi bạn đem một phương thức của Object (VD:
instance.print()) ném vào làm Callback chosetTimeouthoặcaddEventListener(click chuột), thì từ khóathisbên trong nó sẽ bị bứng khỏi gốc, trỏ thẳng ra ngoài khoảng khôngwindowhoặcundefined➔ Lỗi tung toé (Bài 2 - Yêu cầu 3). - Thuốc giải 1:
.bind(obj)(Trói buộc vĩnh viễn): - Cơ học: Sinh ra một bản sao mới của cái hàm đó, nhưng lấy cái dây thừng trói chặt từ khóa
thisvào cáiobjchỉ định. - Code:
setTimeout( instance.print.bind(instance), 100 ). - Thuốc giải 2:
.call(obj, arg1, arg2)và.apply(obj, [args])(Mượn xác hoàn hồn): - Bắt hàm chạy ngay lập tức, ép
thistrỏ vào cái xác mình muốn.calltruyền tham số rời rạc,applyném tham số nguyên 1 mảng.
5. PROTOTYPE (CHUỖI DI TRUYỀN)
Đây là cách JavaScript thiết kế sự Kế thừa. Nó không copy dữ liệu, nó tạo ra một sợi dây vô hình (Prototype Chain) liên kết các Object với nhau để tiết kiệm RAM.
Khởi tạo kế thừa:
Object.create(protoObj)tạo ra một Object mới rỗng tuếch, nhưng có sợi dây nối thẳng vàoprotoObj(Bài 1 - Yêu cầu 2).- Cơ chế dò tìm: Khi bạn gọi
item1.introduce(). - Máy lục tìm chữ
introducetrong ngườiitem1. Không có! - Máy lần theo dây truyền máu lên Object Cha (Prototype). Thấy chữ
introduceở đó ➔ Mượn xài luôn. - Định vị DNA:
Object.getPrototypeOf(obj): Tìm xem bố ruột của cái Object này là ai.Object.hasOwn(obj, "name")hoặcobj.hasOwnProperty("name"): Máy sẽ kiểm tra xem cái thuộc tính"name"này là đồ chính chủ tự có, hay là đồ đi mượn của thằng Cha (Prototype). Trả vềtrue/false(Bài 1 - Yêu cầu 4).