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.age
get
Hi ${object.firstName} ${object.lastName}
注意,当我们在上面添加get
内容时handler
,我们有一些自定义参数。每个可以添加到代理的处理程序都带有一组自定义参数。
所get
使用的函数是get(object, prop, receiver)
:
object
- 原始对象。在上面的例子中,这是包含firstName
、lastName
和 的对象age
prop
- 某人试图 的属性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
[]
.
defineProperty
Object.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