前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[译] JavaScript Proxy -- 一些真实的用例

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

作者头像
腾讯IVWEB团队
发布2020-06-28 09:52:25
3320
发布2020-06-28 09:52:25
举报

原文: Javascript Proxies: Real World Use Case -- Arbaz Siddiqui 译者注, 为了防止出现"鲁棒性"这种因翻译习惯差异导致的混淆, 文中部分术语将不会进行翻译.

Proxy介绍

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

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

根据MDN Web文档

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对象.

Traps(阱)

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

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

Handler(处理器)

Handler是一个所有的Traps生存的占位对象. 简单来说, 可以把它当做一个存放且实现各个traps的对象.

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

代码语言:javascript
复制
//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

输出如下

代码语言:javascript
复制
God
John Travolta
Uma Thurman

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

真实的案例

虽然并不如其他的ES2015的特性那样广为人知, Proxy还是有诸如所有属性的默认值这样的现在看来挺亮眼的用例. 让我们来看看其他的在真实生产环场景中能够利用Proxy的地方.

验证 Validation

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

代码语言:javascript
复制
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";

运行结果如下:

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

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

副作用 Side Effects

我们可以通过Proxy来创建一个在读写属性时的副作用. 出发点在于某些特定的属性被访问或者写入时触发一些函数. 看下面这个例子:

代码语言:javascript
复制
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";

运行结果如下:

代码语言:javascript
复制
sending email after task completion

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

缓存 Caching

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

代码语言:javascript
复制
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);

运行结果如下:

代码语言:javascript
复制
25
undefined

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

缺点

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

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

总结

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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Proxy介绍
  • 术语
    • Target(目标)
      • Traps(阱)
        • Handler(处理器)
        • 真实的案例
          • 验证 Validation
            • 副作用 Side Effects
              • 缓存 Caching
              • 缺点
              • 总结
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档