[译] JavaScript Proxy -- 一些真实的用例

原文: Javascript Proxies: Real World Use Case -- Arbaz Siddiquijavascript

译者注, 为了防止出现"鲁棒性"这种因翻译习惯差别致使的混淆, 文中部分术语将不会进行翻译.java

Proxy介绍

在编程术语范畴中, Proxy指的是帮助/替代另外一个实体(Entity)完成一系列操做的实体. 一个架设在客户端与服务端之间的Proxy服务器分别充当了客户端的服务端服务端的客户端. 对于Proxy来讲, 它们的任务就是介入收到的请求/调用, 并在处理后传递给其上游. 这些介入容许Proxy添加一些额外的业务逻辑或者改变整个操做的行为.git

JavaScript的Proxy从某种意义上来讲是类似的. 它处在代码所操做的对象与实际被操做的对象之间进行处理.github

根据MDN Web文档shell

The Proxy is used to define custom behavior for fundamental operations (e.g. property lookup, assignment, enumeration, function invocation, etc).编程

Proxy被用来自定义一些基础层面的操做(例如属性查找, 赋值, 枚举, 函数调用等)缓存

术语

在完成一个Proxy的使用以前, 有三个术语须要咱们提早进行了解:服务器

Target(目标)

Target就是实际被Proxy操做修改的对象. 它能够是任何一个JavaScript对象.markdown

Traps(阱)

译者注: 这个地方的翻译说实话有点不太好翻译, 但实际上只要可以理解所谓Traps就是用来重载(代理)Target对应的名字的属性/方法的属性/方法就行ide

Traps是指那些在Target的属性或者方法被调用时会介入干涉的方法. 有许多定义了的Traps能够被实现(implement)

Handler(处理器)

Handler是一个全部的Traps生存的占位对象. 简单来讲, 能够把它当作一个存放且实现各个traps的对象.

咱们来看看下面这个例子:

//movie is a target
const movie = {
	name: "Pulp Fiction",
	director: "Quentin Tarantino"
};

//this is a handler
const handler = {
	//get is a trap
	get: (target, prop) => {
		if (prop === 'director') {
			return 'God'
		}
		return target[prop]
	},

	set: function (target, prop, value) {
		if (prop === 'actor') {
			target[prop] = 'John Travolta'
		} else {
			target[prop] = value
		}
	}
};

const movieProxy = new Proxy(movie, handler);

console.log(movieProxy.director); //God

movieProxy.actor = "Tim Roth";
movieProxy.actress = "Uma Thurman";

console.log(movieProxy.actor); //John Travolta
console.log(movieProxy.actress); //Uma Thurman
复制代码

输出以下

God
John Travolta
Uma Thurman
复制代码

上面这个例子中, movie就是咱们所说的Target. 咱们实现了一个拥有setget这两个trap的handler. 在其中咱们添加了两个逻辑: 在访问director时, get这个trap会直接返回God而不是它实际的值; 在对actor赋值时, set这个trap会干涉全部的赋值操做, 并在键为actor时将值改变成John Travlota.

真实的案例

虽然并不如其余的ES2015的特性那样广为人知, Proxy仍是有诸如全部属性的默认值这样的如今看来挺亮眼的用例. 让咱们来看看其余的在真实生产环场景中可以利用Proxy的地方.

验证 Validation

既然咱们已经能够干涉对象的属性赋值过程, 那么咱们能够借此来校验咱们将要赋予给对象属性的值. 看下面这个例子

const handler = {
	set: function (target, prop, value) {
		const houses = ['Stark', 'Lannister'];
		if (prop === 'house' && !(houses.includes(value))) {
			throw new Error(`House ${value} does not belong to allowed ${houses}`)
		}
		target[prop] = value
	}
};

const gotCharacter = new Proxy({}, handler);

gotCharacter.name = "Jamie";
gotCharacter.house = "Lannister";

console.log(gotCharacter);

gotCharacter.name = "Oberyn";
gotCharacter.house = "Martell";
复制代码

运行结果以下:

{ name: 'Jamie', house: 'Lannister' }
Error: House Martell does not belong to allowed Stark,Lannister
复制代码

上面这个例子中, 咱们严格限制了house这个属性所能被赋予的值的范围. 只须要建立一个set的trap, 咱们甚至能用这个实现方式来实现一个只读的对象.

反作用 Side Effects

咱们能够经过Proxy来建立一个在读写属性时的反作用. 出发点在于某些特定的属性被访问或者写入时触发一些函数. 看下面这个例子:

const sendEmail = () => {
	console.log("sending email after task completion")
};


const handler = {
	set: function (target, prop, value) {
		if (prop === 'status' && value === 'complete') {
			sendEmail()
		}
		target[prop] = value
	}
};

const tasks = new Proxy({}, handler);

tasks.status = "complete";
复制代码

运行结果以下:

sending email after task completion
复制代码

这里咱们干涉了status这个属性的写入. 当写入的值是complete时, 会触发一个反作用函数. 在Sindre Sorhuson-change这个包中就一个很Cooooooool的实现.

缓存 Caching

利用介入干涉对象属性读写的能力, 咱们可以建立一个基于内存的缓存. 它只会在值过时前返回值. 看下面这个例子:

const cacheTarget = (target, ttl = 60) => {
	const CREATED_AT = Date.now();
	const isExpired = () => (Date.now() - CREATED_AT) > (ttl * 1000);
	const handler = {
		get: (target, prop) => isExpired() ? undefined : target[prop]
	};
	return new Proxy(target, handler)
};

const cache = cacheTarget({age: 25}, 5);

console.log(cache.age);

setTimeout(() => {
	console.log(cache.age)
}, 6 * 1000);
复制代码

运行结果以下:

25
undefined
复制代码

这里咱们建立了一个函数, 并返回一个Proxy. 在获取target的属性前, 这个Proxy的handler首先会检查target对象是否过时. 基于此, 咱们能够针对每一个键值都设置一个基于TTLs或者其余机制的过时检查.

缺点

虽然Proxy具有一些很神奇的功能, 但在使用时仍然具备一些不得不当心应对的限制:

  1. 性能会受到显著的影响. 在注重性能的代码中应该避免对Proxy的使用
  2. 没有办法区分判断一个对象是一个Proxy的对象或者是target的对象
  3. Proxy可能会致使代码在可读性上面出现问题

总结

Proxy很强, 在很大范围内都可以获得应用, 或者被滥用. 这篇文章中咱们讨论了什么是Proxy, 如何实现一个Proxy, 几个真实案例中的用例, 以及它的缺陷限制.

相关文章
相关标签/搜索