JavaScript - Proxies(代理)



JavaScript 中的代理

JavaScript Proxies(代理) 是允许您包装特定对象并自定义对象的基本操作的对象,例如获取和设置对象属性。简而言之,使用 proxy 对象,您可以向对象添加自定义行为。代理用于实现日志记录、缓存和安全等功能。

我们可以使用 JavaScript 中的 Proxy() 构造函数创建代理对象。构造函数接受两个参数 – target 对象和 handler 对象。它为目标对象返回一个新的代理对象。

语法

以下是在 JavaScript 中创建代理对象的语法 -


 const obj = new Proxy(targetObj, Handler);

在上面的语法中,我们使用了带有 new 关键字的 Proxy() 构造函数。

参数

Proxy 构造函数采用两个参数。

  • targetObj - 它是您要为其创建代理对象并自定义其默认行为的目标对象。
  • Handler - 它是一个对象,其中包含自定义目标对象行为的功能。

在下面的示例中,person 对象包含 name 和 age 属性。

我们已经为名为 proxyObj 的 person 对象定义了代理对象。此外,我们还将处理程序对象作为 Proxy() 构造函数参数传递。

在 handler 对象中,我们定义了用于访问 object 属性的 getter。getter 检查对象是否包含您要查找的属性。如果是,则返回 property 值。否则,它将返回一条消息,指出该对象不包含该属性。


<html>
<body>
	 	<div id = "demo1">The name of the person is: </div>
	 	<div id = "demo2">The height of the person is: </div>
	 	<script>
	 	 	 const person = {
	 	 	 	 	name: "Same",
	 	 	 	 	age: 32,
	 	 	 }
	 	 	 const handler = {
	 	 	 	 	// 定义getter
	 	 	 	 	get: function (object, property) {
	 	 	 	 	 	 return object[property] ? object[property] :	
	 	 	 	 	 	 'Object doesnt contain the property.';
	 	 	 	 	}
	 	 	 }
	 	 	 const proxyObj = new Proxy(person, handler);
	 	 	 document.getElementById("demo1").innerHTML += proxyObj.name;
	 	 	 document.getElementById("demo2").innerHTML += proxyObj.height;
	 	</script>
</body>
</html>

输出

The name of the person is: Same
The height of the person is: Object doesnt contain the property.

当您从对象访问 non-exist 属性时,它将返回 undefined。在这里,我们自定义了对象的默认行为,以返回一个美观且可读的方法。

如果在创建 proxy 对象时传递空 handler 对象,则 proxy 对象的工作方式与原始对象相同。

JavaScript 代理处理程序

JavaScript 中有多个可用的代理处理程序,我们在下面介绍了其中的一些。代理处理程序用于覆盖对象的默认行为。

JavaScript get() 代理处理程序

JavaScript 中的 get() 代理处理程序允许您更改对象的属性访问行为。

语法

按照下面的语法将 get() 代理处理程序与 proxy 对象一起使用。


 get(target, property, receiver)

参数

  • target − 它是一个目标对象。
  • property − 它是需要访问其 value 的属性。
  • receiver − 它本身就是一个代理对象。

在下面的代码中,watch 对象包含 brand、color 和 price 属性。我们已经为 watch 对象创建了代理。

处理程序对象包含 get() 处理程序,如果不是,则返回属性值。否则,它将返回可读消息。


<html>
<body>
	 	<div id = "output1">Brand: 	</div>
	 	<div id = "output2">Price: 	</div>
	 	<script>
	 	 	 const watch = {
	 	 	 	 	brand: "Casio",
	 	 	 	 	color: "Blue",
	 	 	 	 	price: null,
	 	 	 }

	 	 	 const handler = {
	 	 	 	 	get(object, property) {
	 	 	 	 	 	 return object[property] != null ? object[property] : "Property is null.";
	 	 	 	 	}
	 	 	 }
	 	 	 const wathcProxy = new Proxy(watch, handler);
	 	 	 document.getElementById("output1").innerHTML += wathcProxy.brand;
	 	 	 document.getElementById("output2").innerHTML += wathcProxy.price;
	 	</script>
</body>
</html>

输出

Brand: Casio
Price: Property is null.

JavaScript set() 代理处理程序

JavaScript 中的 set() 代理处理程序用于更改更新对象属性的默认行为。

语法

按照下面的语法来使用 set() 代理处理程序。


 set(target, property, value, receiver)

参数

  • target − 它是一个目标对象。
  • property − 更改其值的属性。
  • value − 它是一个更新的值。
  • receiver − 它本身就是一个代理对象。

在下面的代码中,处理程序对象包含 set() 代理处理程序。set() 处理程序检查属性是否等于 'price'。如果是,则使用新值更新属性值。否则,它将 'Not Available' 设置为 object 属性。


<html>
<body>
	 	<p id = "demo"> </p>
	 	<script>
	 	 	 const output = document.getElementById("demo");
	 	 	 const watch = {
	 	 	 	 	brand: "Casio",
	 	 	 	 	color: "Blue",
	 	 	 	 	price: null,
	 	 	 }
	 	 	 const handler = {
	 	 	 	 	set(object, property, value) {
	 	 	 	 	 	 if (property === "price") {
	 	 	 	 	 	 	 	object[property] = value;
	 	 	 	 	 	 }
	 	 	 	 	 	 else {
	 	 	 	 	 	 	 	object[property] = "Not Available";
	 	 	 	 	 	 }
	 	 	 	 	}
	 	 	 }
	 	 	 const wathcProxy = new Proxy(watch, handler);
	 	 	 wathcProxy.price = 2000;
	 	 	 wathcProxy.dial = "Round";
	 	 	 output.innerHTML += "Price: " + wathcProxy.price + "<br>";
	 	 	 output.innerHTML += "Dial: " + wathcProxy.dial + "<br>";
	 	</script>
</body>
</html>

输出

Price: 2000
Dial: Not Available

JavaScript apply() 代理处理程序

apply() 代理处理程序用于更改函数调用的默认行为。

语法

按照下面的语法使用 apply() 代理处理程序。


 apply(target, thisArg, arguments)

参数

  • target − 需要执行的目标函数。
  • thisArg − 指的是应该在函数体中使用 this 关键字访问其元素的上下文。
  • arguments − 要传递给函数的参数数组。

在下面的代码中,getDetails 是为 getWatchDetails() 函数创建的代理对象。处理程序对象包含 apply() 方法并调用 target 函数。

我们通过将 watch 对象作为参数传递来调用 getDetails() 代理。


<html>
<body>
	 	<p id = "output"> </p>
	 	<script>
	 	 	 const watch = {
	 	 	 	 	brand: "Casio",
	 	 	 	 	color: "Blue",
	 	 	 	 	price: 2000,
	 	 	 }
	 	 	 const getWatchDetails = function (watch) {
	 	 	 	 	return `Brand: ${watch.brand}, 	
	 	 	 	 	Color: ${watch.color},	
	 	 	 	 	price: ${watch.price}`;
	 	 	 }
	 	 	 const getDetails = new Proxy(getWatchDetails, {
	 	 	 	 	apply(target, thisArg, args) {
	 	 	 	 	 	 return target(...args).toUpperCase();
	 	 	 	 	}
	 	 	 });
	 	 	 document.getElementById("output").innerHTML += getDetails(watch);
	 	</script>
</body>
</html>

输出

BRAND: CASIO, COLOR: BLUE, PRICE: 2000

JavaScript 中 Proxy 对象的使用

在这里,我们通过示例解释了使用 proxy 对象的好处。

用于验证

您可以在 JavaScript 中使用代理对象来验证属性值,同时更新属性的值或向对象添加新属性。

在下面的代码中,numbers 对象包含 num1、num2 和 num3 属性。set() 代理处理程序检查新值是否大于当前值。如果是,则更新该值。否则,它将保留旧值。


<html>
<body>
	 	<p id = "demo"> </p>
	 	<script>
	 	 	 const output = document.getElementById("demo");
	 	 	 const numbers = {
	 	 	 	 	num1: 10,
	 	 	 	 	num2: 20,
	 	 	 	 	num3: 30,
	 	 	 }
	 	 	 const handler = {
	 	 	 	 	set(object, property, value) {
	 	 	 	 	 	 if (value > object[property]) {	
	 	 	 	 	 	 	 	// 使用以前的值验证新值
	 	 	 	 	 	 	 	object[property] = value;
	 	 	 	 	 	 }
	 	 	 	 	}
	 	 	 }
	 	 	 const numberProxy = new Proxy(numbers, handler);
	 	 	 numberProxy.num1 = 20;
	 	 	 numberProxy.num2 = 10;

	 	 	 output.innerHTML += "num1: " + numbers.num1 + ", num2: " + numbers.num2;
	 	</script>
</body>
</html>

输出

num1: 20, num2: 20

用于访问控制

您还可以使用代理处理程序来控制 JavaScript 中对象的访问。例如,您可以限制用户更新对象属性并将其设置为只读。

在下面的代码中,每当您尝试更新对象属性值时,它都会打印该对象是只读的消息。


<html>
<body>
	 	<p id = "demo"> </p>
	 	<script>
	 	 	 const output = document.getElementById("demo");
	 	 	 const numbers = {
	 	 	 	 	num1: 10,
	 	 	 	 	num2: 20,
	 	 	 	 	num3: 30,
	 	 	 }
	 	 	 const handler = {
	 	 	 	 	set(object, property, value) {
	 	 	 	 	 	 output.innerHTML += "Object is read-only.<br>"
	 	 	 	 	}
	 	 	 }
	 	 	 const numberProxy = new Proxy(numbers, handler);
	 	 	 numberProxy.num1 = 20;
	 	 	 output.innerHTML += "num1: " + numberProxy.num1;
	 	</script>
</body>
</html>

输出

Object is read-only.
num1: 10

副作用

当任何人尝试访问或更新 object 属性时,您可以调用函数或类方法。

在下面的示例中,emailValidator() 函数检查电子邮件是否包含 '@'。如果是,则返回 true。否则,它将返回 false。

在 set() 代理处理程序中,我们根据 emailValidator() 函数的返回值更新属性值。


<html>
<body>
	 	<p id = "output"> </p>
	 	<script>
	 	 	 const emails = {
	 	 	 	 	email1: "abcd@gmail.com",
	 	 	 }
	 	 	 // 验证电子邮件的功能
	 	 	 function emailValidator(email) {
	 	 	 	 	if (email.includes("@")) {
	 	 	 	 	 	 return true;
	 	 	 	 	} else {
	 	 	 	 	 	 return false;
	 	 	 	 	}
	 	 	 }
	 	 	 const handler = {
	 	 	 	 	set(object, property, value) {
	 	 	 	 	 	 if (emailValidator(value)) {
	 	 	 	 	 	 	 	object[property] = value;
	 	 	 	 	 	 }
	 	 	 	 	}
	 	 	 }
	 	 	 const emailProxy = new Proxy(emails, handler);
	 	 	 emailProxy.email1 = "nmc@gmail.com";
	 	 	 document.getElementById("output").innerHTML =	
	 	 	 "email1: " + emailProxy.email1;
	 	</script>
</body>
</html>

输出

email1: nmc@gmail.com

但是,代理处理程序的使用不受限制,我们无法在本教程中介绍每个用例。因此,您可以探索代理处理程序的更多用例。

JavaScript 代理处理程序列表

在这里,我们列出了 JavaScript 中的所有代理处理程序。

代理处理程序方法

Proxy Handler 描述
apply() 更改了函数调用的默认行为。
construct() 捕获新对象的构造。
defineProperty() 更改定义新属性的行为。
deleteProperty() 更改删除新属性的行为。
get() 更改访问对象属性的行为。
getOwnPropertyDescriptor() 捕获对象的 getOwnPropertyDescriptor() 方法。
getPrototypeOf() 捕获内部方法。
has() 操作对象是否包含属性的检查。
isExtensible() 捕获 Object 的 isExtensible() 方法。
ownKeys() 更改 ownKeys() 方法的行为。
preventExtension() 捕获阻止对象的扩展。
set() 更改添加新属性或更新对象的属性值的默认行为。
setPrototypeOf() 自定义 Object.setPrototypeOf() 方法。

构造 函数

构造 函数 描述
Proxy() 用于创建代理对象。

静态方法

方法 描述
revocable() 还用于创建类似于 Proxy() 构造函数的新代理对象。