JavaScript - Proxies: Hướng dẫn cho người mới bắt đầu
Xin chào các bạn đang học lập trình! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới của JavaScript Proxies. Đừng lo lắng nếu bạn chưa từng nghe về chúng trước đây - chúng ta sẽ bắt đầu từ những điều cơ bản nhất và dần dần nâng cao. Cuối cùng của bài hướng dẫn này, bạn sẽ thành thạo trong nghệ thuật sử dụng Proxies như một chuyên gia!
Proxy là gì trong JavaScript?
Hãy bắt đầu với một ví dụ đơn giản. Hãy tưởng tượng bạn là một ngôi sao (vì sao không mơ lớn, phải không?). Bạn nổi tiếng đến mức cần có một người khác quản lý các cuộc hẹn, thư từ fan hâm mộ và các hoạt động hàng ngày khác. Người này hành động thay mặt bạn được gọi là proxy trong thế giới thực.
Trong JavaScript, một Proxy hoạt động theo cách tương tự. Nó là một đối tượng bao quanh một đối tượng khác (hãy gọi nó là đối tượng đích) và có thể chặn và định nghĩa lại các thao tác cơ bản cho đối tượng đó. Đ酷, phải không?
Dưới đây là một ví dụ cơ bản để bắt đầu:
let target = {
name: "John Doe",
age: 30
};
let handler = {
get: function(target, property) {
console.log(`Getting the ${property} property`);
return target[property];
}
};
let proxy = new Proxy(target, handler);
console.log(proxy.name);
Nếu bạn chạy đoạn mã này, bạn sẽ thấy:
Getting the name property
John Doe
Hãy phân tích này:
- Chúng ta có một đối tượng
target
với các thuộc tínhname
vàage
. - Chúng ta tạo một đối tượng
handler
với một trapget
(chúng ta sẽ nói nhiều hơn về traps sau). - Chúng ta tạo một
proxy
sử dụng cấu trúcProxy
, truyềntarget
vàhandler
. - Khi chúng ta cố gắng truy cập
proxy.name
, trapget
của chúng ta sẽ được kích hoạt, ghi lại một tin nhắn trước khi trả về giá trị thực tế.
JavaScript Proxy Handlers
Bây giờ chúng ta đã thử nghiệm một chút, hãy cùng lặn sâu hơn vào Proxy handlers. Một handler là một đối tượng định nghĩa các trap cho Proxy của chúng ta. Các trap là các phương thức cung cấp truy vấn thuộc tính, gán thuộc tính, liệt kê thuộc tính, gọi hàm, v.v.
Dưới đây là bảng một số trap handler phổ biến:
Trap | Mô tả |
---|---|
get | Chặn truy cập thuộc tính |
set | Chặn gán thuộc tính |
has | Chặn toán tử in
|
deleteProperty | Chặn toán tử delete
|
apply | Chặn gọi hàm |
construct | Chặn toán tử new
|
Hãy xem một ví dụ đầy đủ hơn sử dụng nhiều trap:
let target = {
name: "Alice",
age: 25
};
let handler = {
get: function(target, property) {
console.log(`Accessing the ${property} property`);
return target[property];
},
set: function(target, property, value) {
console.log(`Setting the ${property} property to ${value}`);
target[property] = value;
return true;
},
has: function(target, property) {
console.log(`Checking if ${property} exists`);
return property in target;
}
};
let proxy = new Proxy(target, handler);
console.log(proxy.name); // Kích hoạt trap get
proxy.job = "Developer"; // Kích hoạt trap set
console.log("age" in proxy); // Kích hoạt trap has
Chạy đoạn mã này sẽ ra output:
Accessing the name property
Alice
Setting the job property to Developer
Checking if age exists
true
Đó có phải là tuyệt vời không? Proxy của chúng ta bây giờ đã chặn truy cập thuộc tính, gán thuộc tính và toán tử in
!
Các ứng dụng của Proxy Object trong JavaScript
Bạn có thể đang tự hỏi, "Đây là cool và tất cả, nhưng tôi sẽ thực sự sử dụng nó khi nào?" Câu hỏi tuyệt vời! Proxies có nhiều ứng dụng thực tế:
- Validation: Bạn có thể sử dụng Proxies để xác thực dữ liệu trước khi nó được đặt trên một đối tượng.
let user = {
name: "Bob",
age: 30
};
let validator = {
set: function(obj, prop, value) {
if (prop === "age") {
if (typeof value !== "number") {
throw new TypeError("Age must be a number");
}
if (value < 0 || value > 120) {
throw new RangeError("Age must be between 0 and 120");
}
}
obj[prop] = value;
return true;
}
};
let proxiedUser = new Proxy(user, validator);
proxiedUser.age = 25; // This works
try {
proxiedUser.age = "thirty"; // This throws a TypeError
} catch (e) {
console.log(e.message);
}
try {
proxiedUser.age = 150; // This throws a RangeError
} catch (e) {
console.log(e.message);
}
- Logging: Bạn có thể sử dụng Proxies để ghi lại truy cập vào thuộc tính của đối tượng.
let target = {
name: "Charlie",
age: 35
};
let logger = {
get: function(target, property) {
console.log(`Property ${property} accessed at ${new Date()}`);
return target[property];
}
};
let proxiedTarget = new Proxy(target, logger);
console.log(proxiedTarget.name);
console.log(proxiedTarget.age);
- Default Values: Bạn có thể sử dụng Proxies để cung cấp giá trị mặc định cho các thuộc tính không tồn tại.
let handler = {
get: function(target, property) {
return property in target ? target[property] : "Property not found";
}
};
let proxy = new Proxy({}, handler);
proxy.name = "David";
console.log(proxy.name); // Outputs: David
console.log(proxy.age); // Outputs: Property not found
JavaScript Proxy Handlers List
Để kết thúc hành trình của chúng ta vào thế giới của JavaScript Proxies, hãy cùng xem qua danh sách đầy đủ tất cả các trap handler khả dụng:
Handler Trap | Mô tả |
---|---|
get | Chặn lấy giá trị thuộc tính |
set | Chặn đặt giá trị thuộc tính |
has | Chặn toán tử in
|
deleteProperty | Chặn toán tử delete
|
apply | Chặn gọi hàm |
construct | Chặn toán tử new
|
getPrototypeOf | Chặn Object.getPrototypeOf
|
setPrototypeOf | Chặn Object.setPrototypeOf
|
isExtensible | Chặn Object.isExtensible
|
preventExtensions | Chặn Object.preventExtensions
|
getOwnPropertyDescriptor | Chặn Object.getOwnPropertyDescriptor
|
defineProperty | Chặn Object.defineProperty
|
ownKeys | Chặn Object.getOwnPropertyNames và Object.getOwnPropertySymbols
|
Và thế là xong! Chúng ta đã xem qua các основ của JavaScript Proxies, khám phá một số ứng dụng thực tế và thậm chí xem qua tất cả các trap handler khả dụng. Nhớ rằng, như bất kỳ công cụ mạnh mẽ nào, Proxies nên được sử dụng một cách thận trọng. Chúng rất hữu ích cho một số trường hợp sử dụng, nhưng chúng cũng có thể gây ra hiệu suất thấp.
Hy vọng rằng bài hướng dẫn này đã giúp bạn hiểu rõ hơn về JavaScript Proxies. Hãy tiếp tục thực hành, tiếp tục lập trình, và trước khi bạn biết, bạn sẽ thành thạo Proxy như một chuyên gia! Chúc bạn lập trình vui vẻ!
Credits: Image by storyset