JavaScript - 代理:初學者指南

Hello there, aspiring programmers! Today, we're going to embark on an exciting journey into the world of JavaScript Proxies. Don't worry if you've never heard of them before – we'll start from the very basics and work our way up. By the end of this tutorial, you'll be well-versed in the art of using Proxies like a pro!

JavaScript - Proxies

什麼是 JavaScript 中的代理?

讓我們從一個簡單的比喻開始。想像你是一位名人(因為為什麼不夢想大一些呢?),你非常出名,需要有人來處理你的約會、粉絲信和其他日常活動。在現實世界中,這個代表你行事的人稱為代理。

在 JavaScript 中,代理的工作方式與此類似。它是一個包絡另一個對象(我們稱之為目標對象)的對象,並可以截獲和重新定義該對象的基本操作。酷炫吧?

以下是一個基本示例來讓我們開始:

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);

如果你運行這段代碼,你會看到:

Getting the name property
John Doe

讓我們分解一下:

  1. 我們有一個帶有 nameage 屬性的 target 對象。
  2. 我們創建一個帶有 get 拦截器(我們稍後會更多談論拦截器)的 handler 對象。
  3. 我們使用 Proxy 构造函数創建一個 proxy,傳入我們的 targethandler
  4. 當我們嘗試訪問 proxy.name 時,我們的 get 拦截器被觸發,在返回實際值之前記錄一條消息。

JavaScript 代理处理器

現在我們已經略嘗勝味,讓我們更深入地研究一下代理处理器。处理器是一個定義了我們代理的拦截器的對象。拦截器是提供屬性查找、賦值、枚舉、函數調用等方法的方法。

以下是一些常見的处理器拦截器表格:

拦截器 描述
get 截獲屬性訪問
set 截獲屬性賦值
has 截獲 in 運算符
deleteProperty 截獲 delete 運算符
apply 截獲函數調用
construct 截獲 new 運算符

讓我們看一下一個使用多個拦截器的更全面的示例:

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);  // Triggers get trap
proxy.job = "Developer";  // Triggers set trap
console.log("age" in proxy);  // Triggers has trap

運行這段代碼將輸出:

Accessing the name property
Alice
Setting the job property to Developer
Checking if age exists
true

這不是很棒嗎?我們的代理現在正在截獲屬性訪問、賦值和 in 運算符!

JavaScript 代理對象的使用

你可能會想,"這很酷,但我在什麼時候會真正使用這個?" 好問題!代理在以下幾個實際應用中非常有用:

  1. 驗證:你可以使用代理在數據設置到對象上之前進行驗證。
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);
}
  1. 日誌記錄:你可以使用代理來記錄對象屬性的訪問。
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);
  1. 默認值:你可以使用代理為不存在的屬性提供默認值。
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 代理处理器列表

讓我們結束我們對 JavaScript 代理的探索,看一下所有可用的处理器拦截器列表:

处理器拦截器 描述
get 截獲獲取屬性值
set 截獲設置屬性值
has 截獲 in 運算符
deleteProperty 截獲 delete 運算符
apply 截獲函數調用
construct 截獲 new 運算符
getPrototypeOf 截獲 Object.getPrototypeOf
setPrototypeOf 截獲 Object.setPrototypeOf
isExtensible 截獲 Object.isExtensible
preventExtensions 截獲 Object.preventExtensions
getOwnPropertyDescriptor 截獲 Object.getOwnPropertyDescriptor
defineProperty 截獲 Object.defineProperty
ownKeys 截獲 Object.getOwnPropertyNamesObject.getOwnPropertySymbols

就这样!我們已經介紹了 JavaScript 代理的基本知識,探索了一些實際用途,甚至還看過所有可用的处理器拦截器。記住,像任何强大的工具一樣,代理應該謹慎使用。它們在某些使用場景中非常有用,但它們確實會帶來性能開銷。

希望這個教程能夠幫助你解開 JavaScript 代理的神秘面紗。持續練習,持續編程,在你知曉之前,你會變得像專家一樣使用代理!快樂編程!

Credits: Image by storyset