JavaScript 代理:像专业人士一样使用 JavaScript 代理
代理是 JavaScript 中的对象,它允许你创建对象的代理,同时还可以为诸如get、set和 之类的标准对象操作定义自定义行为has。这意味着,例如,当有人尝试从对象中获取属性值时,你可以定义一组自定义行为。这使得代理成为一个非常强大的工具,让我们来看看它是如何工作的。
JavaScript 代理的基础知识
上面的内容听起来很复杂,所以让我们先看一个没有任何方法的简单示例。可以使用构造函数创建代理new Proxy(),该构造函数接受两个参数:
- ,
target即原始对象。 - ,这
handler是我们将添加到对象顶部的一组方法或属性。
可以handler包含一系列预定义方法。get例如,如果我们为 定义一个方法,它将自定义当我们尝试get从对象中获取某个项时发生的情况。
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
get: (object, prop) => {
console.log(`Hi ${object.firstName} ${object.lastName}`)
}
}
let proxyExample = new Proxy(target, handler);
proxyExample.age; // console logs "Hi John Doe"
由于我们尝试在代理上get获取 的值,自定义处理程序被触发 - 因此我们在控制台中输出了。如你所见,这可以成为一个非常强大的工具,因为当调用对象的标准操作时,你可以做各种各样的事情。proxyExample.agegetHi ${object.firstName} ${object.lastName}
注意,当我们在上面添加get内容时handler,我们有一些自定义参数。每个可以添加到代理的处理程序都带有一组自定义参数。
所get使用的函数是get(object, prop, receiver):
object- 原始对象。在上面的例子中,这是包含firstName、lastName和 的对象ageprop- 某人试图 的属性get。在上面的例子中,age。reciever- 代理本身。
更新代理值
代理仍然引用原始对象,因此对象值和代理值的引用是相同的。因此,如果您尝试更新代理的值,它也会更新原始对象的值。例如,下面我尝试更新代理,如您所见,原始对象和代理对象都被更新了:
let target = {
name: "John",
age: 152
}
let handler = {
}
let proxyExample = new Proxy(target, handler);
proxyExample.name = "Dave";
console.log(proxyExample.name); // Console logs Dave
console.log(target.name); // Console logs Dave
了解这一点很有用 - 不要指望代理会完全创建一个单独的对象 - 它不是复制对象的方法。
JavaScript 代理中的自定义处理程序
代理有许多自定义处理程序,允许我们“捕获”任何对象操作并对其进行一些有趣的操作。最常用的方法如下:
proxy.apply(objects, thisObject, argList)- 一种捕获函数调用的方法。proxy.construct(object, argList, newTarget)- 使用构造函数关键字调用函数时捕获的方法new。proxy.defineProperty(object, prop, descriptor)- 当使用 向对象添加新属性时,用于捕获的方法Object.defineProperty。proxy.deleteProperty(object, prop)- 当从对象中删除属性时捕获的方法。proxy.get(object, prop, receiver)- 如前所述,当有人试图get从对象中获取属性时,采用这种方法进行捕获。proxy.set(object, prop, value, receiver)- 当属性被赋予值时捕获的方法。proxy.has(object, prop)- 一种诱捕in操作员的方法。
上述方法足以完成你想用代理做的所有事情。它们很好地覆盖了所有主要的对象操作,你可以根据需要进行修改和自定义。
不过,还有一些其他操作 - 除了这些非常基本的对象操作之外,我们还可以访问:
proxy.getPrototypeOf(object)- 一种捕获Object.getPrototypeOf方法的方法。proxy.getOwnPropertyDescriptor(object, prop)- 一种捕获的方法getOwnPropertyDescriptor,它返回特定属性的描述符 - 例如,它是否可枚举等。proxy.isExtensible(object)Object.isExtensible()- 一种在触发时进行捕获的方法。proxy.preventExtensions(object)Object.preventExtensions()- 一种在触发时进行捕获的方法。proxy.setPrototypeOf(object, prototype)Object.setPrototypeOf()- 一种在触发时进行捕获的方法。proxy.ownKeys(object)- 当类似方法被触发时捕获的方法Object.getOwnPropertyNames()。
让我们更详细地了解其中的一些内容,以了解代理的工作原理。
将 in 运算符与代理一起使用
我们已经讲过了proxy.get(),现在让我们来看看has()。这主要在使用in运算符时触发。例如,如果我们想在in使用 时在控制台记录某个属性不存在的事实,我们可以这样做:
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
has: (object, prop) => {
if(object[prop] === undefined) {
console.log('Property not found');
}
return object[prop]
}
}
let proxyExample = new Proxy(target, handler);
console.log('address' in proxyExample);
// console logs
// 'Property not found'
// false
由于address未在中定义target(因此在中proxyExample),尝试控制台记录'address' in proxyExample将返回 false - 但它也会控制台记录'Property not found',因为我们在代理中定义了它。
使用代理设置值
您可能想要修改的一个同样有用的方法是set()。下面,我使用自定义set处理程序来修改尝试更改用户年龄时发生的情况。对于每个集合操作,如果属性是数字,那么我们将在数字更新时在控制台中记录差异。
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
set: (object, prop, value) => {
if(typeof object[prop] === "number" && typeof value === "number") {
console.log(`Change in number was ${value - object[prop]}`);
}
return object[prop]
}
}
let proxyExample = new Proxy(target, handler);
proxyExample['age'] = 204;
// Console logs
// Change in number was 52
由于proxyExample.age更新后的值204都是数字,我们不仅将值更新为204,而且还会收到一个有用的控制台日志,告诉我们这两个数字之间的差值。很酷吧?
虽然set会对任何集合操作触发,包括向对象添加新项,但你也可以使用 实现类似的行为defineProperty。例如,下面这样也可以:
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
defineProperty: (object, prop, descriptor) => {
console.log(`A property was set - ${prop}`);
},
}
let proxyExample = new Proxy(target, handler);
proxyExample['age'] = "123 Fake Street";
// Console logs
// A property was set - address
但是请注意,如果您添加set和defineProperty都作为处理程序,则在我们使用方括号或符号设置属性的情况下set将会覆盖。但是,如果您明确使用,仍然会触发,如下所示:defineProperty[].definePropertyObject.defineProperty
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
defineProperty: (object, prop, descriptor) => {
console.log(`A property was set with defineProperty - ${prop}`);
return true;
},
set: (object, prop, descriptor) => {
console.log(`A property was set - ${prop}`);
return true;
},
}
let proxyExample = new Proxy(target, handler);
Object.defineProperty(proxyExample, 'socialMedia', {
value: 'twitter',
writable: false
});
proxyExample['age'] = "123 Fake Street";
// Console logs
// A property was set with defineProperty - socialMedia
// A property was set - address
使用代理删除值
除了这些实用的方法之外,我们还可以用它deleteProperty来处理用户使用delete关键字删除某些内容时发生的情况。例如,我们可以在控制台中记录日志,告知用户属性正在被删除:
let target = {
firstName: "John",
lastName: "Doe",
age: 152
}
let handler = {
deleteProperty: (object, prop) => {
console.log(`Poof! The ${prop} property was deleted`);
},
}
let proxyExample = new Proxy(target, handler);
delete proxyExample['age'];
// Console logs
// Poof! The age property was deleted
使用代理自定义函数调用
代理还允许我们在调用函数时运行自定义代码。这是因为 JavaScript 的特性:函数本身就是对象。有两种方法可以做到这一点:
- 使用
apply()处理程序来捕获标准函数调用。 - 使用
construct()处理程序来捕获new构造函数调用。
这是一个简单的例子,我们捕获一个函数调用,并通过在其输出末尾附加一些内容来修改它。
let target = (firstName, lastName) => {
return `Hello ${firstName} ${lastName}`
}
let handler = {
apply: (object, thisObject, argsList) => {
let functionCall = object(...argsList);
return `${functionCall}. I hope you are having a nice day!`
},
}
let proxyExample = new Proxy(target, handler);
proxyExample("John", "Doe");
// Returns
// Hello John Doe. I hope you are having a nice day!
apply接受三个参数:
object- 原始对象。thisObject-this函数/对象的值。argsList- 传递给函数的参数。
object上面,我们使用包含原始函数的参数调用了我们的函数target。然后我们在参数末尾添加了一些文本来改变函数的输出。是不是很酷?
我们也可以使用 做同样的事情construct,它也有三个参数:
object- 原始对象。argsList- 函数/对象的参数。newTarget- 最初调用的构造函数 - 即代理。
construct下面是一个函数返回对象的示例,我们使用代理上的方法向其添加一些属性:
function target(a, b, c) {
return {
a: a,
b: b,
c: c
}
}
let handler = {
construct: (object, argsList, newTarget) => {
let functionCall = object(...argsList);
return { ...functionCall, d: 105, e: 45 }
},
}
let proxyExample = new Proxy(target, handler);
new proxyExample(15, 24, 45);
// Returns
// {a: 15, b: 24, c: 45, d: 105, e: 45}
结论
代理是 JavaScript 工具库中一个非常棒的工具,它允许你修改对象的基本操作。代理提供了大量的方法供你使用,如果使用得当,它们可以大大简化你的代码。希望你喜欢这篇文章——你可以在这里阅读更多我的 JavaScript 内容。
鏂囩珷鏉ユ簮锛�https://dev.to/smpnjn/using-javascript-proxies-like-a-pro-590
后端开发教程 - Java、Spring Boot 实战 - msg200.com