VillainHR

超好用的es6 proxy

proxy reflect es6 2016-07-29

es6中的proxy. 实际上,就相当于重载. 即,在不改变原对象的属性的前提下, 使用proxy 直接重新定义一个新的对象。

基本语法

我们来看一下,proxy的基本语法格式:

// @param {Object} target 用来被代理的对象
// @param {Object} handler 用来设置代理的对象
var proxy = new Proxy(target, handler);

proxy的原理就是通过handler中,重载的方法,阻止掉获取target的内容. 其,最常用的地方,就在于它的一个middleware的特性. 因为,在js中,经常会对数据格式进行验证. 这里,就可以使用proxy来进行替换. 所以, proxy经常是用来处理数据的接口操作的.

替换get/set

替换get和set是proxy的老本行, 我们需要熟练的掌握,proxy如何进行代理的.

# 替换get
const target = {  
    name: 'Billy Bob',
    age: 15
};

const handler = {  
// @param {Object} target 就是被proxy的对象
// @param {String} key 通过proxy 访问的键名
// @param {proxy} proxy 代理过后传递的对象
    get(target, key, proxy) {
        const today = new Date();
        console.log(`GET request made for ${key} at ${today}`);
        // Reflect实际上,是Object的一个替代.
        return Reflect.get(target, key, proxy);
    }
};

const proxy = new Proxy(target, handler);

Reflect补充

这里,稍微补充点Reflect的知识. Reflect是为了更好的统一obj操作而诞生的. 因为 Object.prototype上面某些定义的方法,行为太不统一了, 所以,为了弥补这个缺陷es6提出了reflect这个 trick. Reflect常用的方法一共有13个:

  • Reflect.apply(target,thisArg,args) *
  • Reflect.construct(target,args) *
  • Reflect.get(target,name,receiver) *
  • Reflect.set(target,name,value,receiver) *
  • Reflect.defineProperty(target,name,desc)
  • Reflect.deleteProperty(target,name)
  • Reflect.has(target,name) *
  • Reflect.ownKeys(target)
  • Reflect.isExtensible(target)
  • Reflect.preventExtensions(target)
  • Reflect.getOwnPropertyDescriptor(target, name)
  • Reflect.getPrototypeOf(target)
  • Reflect.setPrototypeOf(target, prototype)

其中,打*的就是比较常用的。 具体的含义,可以参考Reflect描述. 这里,主要说一下和Proxy有关的内容.即,get,set

Reflect.get|set

它俩的基本格式实际上差不多:

Reflect.get(target,name,receiver) 
Reflect.set(target,name,value,receiver)

具体的行为,有点让人灰解。let’s take a look 最简单的获取就是通过

var target = {
	b:1
}
// 获取target.b的内容
console.log(Reflect.get(target,"b")); // 返回1

当然,一般人也不会这么用. 关键他有个receiver.(什么鬼?)

var target = {
	get b(){return this.a},
	a:1,
}
var receiver = {
	a:2
}
// 如果设置了receiver, 并且target中设置了b的get 返回. 则将this替换为receiver
console.log(Reflect.get(target,"b",receiver));

简单的来说就是:

  • b 是 get(){}函数
  • get里面的this变为receiver

同理, 如果你调用的是Reflect.set则.一样的.

var target = {
    // 这里的this指的是receiver
	set b(val){this.a=val},
	a:1,
}
var receiver = {
	a:2
}
console.log(Reflect.set(target,"b",4,receiver)); // true
console.log(receiver.a); //4
console.log(target.a); //1

Relect.has()

这个方法和in操作符,两者的关系其实差别并不大. 他就是用来判断对应的简直是否存在在obj中的.

var receiver = {
	a:2
}
// 使用原来的in
console.log('a' in receiver); // 返回true
// 使用Reflect.has
console.log(Reflect.has(receiver,'a')); // 返回true

这尼玛有意义吗? 实际上,这是js布的一个局, 应为js以后想朝着函数编程进行, 对于命令式的编程语法,有点想要放弃的节奏.

Reflect.apply(target,thisArg,args)

该方法的出现初衷是为了更好的解决,apply方法的唯一性的.因为,考虑到apply方法有可能已经被定义过了(我就想问谁会这么无聊?) Reflect 为了避免这样的情况,就提出了apply方法,让你完全不用担心apply被占用,并且完美的触发函数.

// 原生手动触发apply方法
fn.apply(this,args) // args我数组参数
// 使用Reflect.apply触发
Reflect.apply(fn,this,args)

ok, Reflect我们差不多就了解到这里. 我们回过头去看一下Proxy内容。

Proxy.get

这里,我们将上面写的get方法,再搬一遍~

# 替换get
const target = {  
    name: 'Billy Bob',
    age: 15
};
const handler = {  
// @param {Object} target 就是被proxy的对象
// @param {String} key 通过proxy 访问的键名
// @param {proxy} proxy 代理过后传递的对象
    get(target, key, proxy) {
        const today = new Date();
        console.log(`GET request made for ${key} at ${today}`);
        // Reflect实际上,是Object的一个替代.
        return Reflect.get(target, key, proxy);
    }
};
const proxy = new Proxy(target, handler);

使用Proxy代理一层的意义关键在于,他能够将和数据提取无关的操作给分离出来, 让业务层在业务层,数据层在数据层. 清晰易懂,可爱明了,so clear so clear.

写log

使用Proxy重新定义的get, 一个比较多的用处就是,对敏感数据用户访问设置log信息.

const handler = {
	get(target,key,proxy){
		if(key==="sensitive"){
			record.log(new Date(),'\n','yourname fetch this data')
		}
		return Reflect.get(target,key,proxy);
	}
}

是不是感觉灰常简单. 另外,我们还可以把邮件通知,也加在里面.

指定用户访问

假如某一天,你的leader说:"小明啊,我们这个数据可了不得,现在不能让所有人访问了, 只有你和我还有小丽可以看. 这应该不难吧~" 艹… u can u up 没事,只要有了proxy之后,我们以前所有的业务逻辑代码都不用变,只需要加一层即可.

// 敏感数据
let data = {
	lovers:"sensitive message",
	asset:"sensitive message"
}
let account = ['小明','boss','小丽'];
data = new Proxy(data,{
	get(target,key,proxy){
		return function(name){
			if(account.includes(name)){
				return Reflect.get(target,key,proxy);
			}
			return 'u are a villain'
		}
	}
});
console.log(data.lovers("小明")); // 合法访问
console.log(data.lovers()); // 非法访问 返回 u are a villain

将获取对象的property 修改为一个函数方法. 然后在参数里面指定具体需要的全新啊.

proxy.set

proxy.set的格式和get的歌格式真的很相似. 就多了一个value

// 重写set
let obj = {
	param:1
}
obj = new Proxy(obj,{
	set(target,key,val,proxy){
		console.log('overload set');
		return Reflect.set(target,key,val,proxy);
	}
})

obj.param = 2;
console.log(obj.param); // 返回2

而对于Proxy.set最常用的地方就在于验证了

数据验证

这里,通常适用在我们接受用户的输入,然后进行验证的过程. 以前,我们需要硬生生在原有业务逻辑里面,嵌套灰常多的validator逻辑,但是,现在,我们只需要在Proxy一层里面,定义好相关的验证即可.

// 重写set
let obj = {
	param:1
}
obj = new Proxy(obj,{
	set(target,key,val,proxy){
		if(key==='param'){
			if(!/^\d+$/.test(val)) throw new Error('只能为数字');
		}
		return Reflect.set(target,key,val,proxy);
	}
})

try{
	obj.param = "1fds";
}catch(e){
	console.log(e);
	console.log('无效数据');
}

如果,你嫌在set里面有太多的if判断,你也可以自己使用策略者模式,来写一下验证规则.

proxy.revocable()

proxy的revocable的意思是,取消访问proxy 的权限. 这个有什么用呢? 没有什么卵用… 开玩笑的. 他的映射范围应该算是比较少的.但是往往却是精华. 现在,我们有这样一个场景,假如,你现在有一个敏感数据, 但如果突然发生错误,你想立即取消掉对该数据的访问,so? 一般,要不就是直接删除该数据. 但这样方式,太简单粗暴了. 而,proxy提供了一个完美的trick来帮助我们实现这一方式. 使用revocable()方法. 这时, 定义一个proxy就需要使用 Proxy.revocable(target, handler);

// 这里,就不需要使用new Proxy()这种方式来定义
// 而,需要使用Proxy.revocalble(target,handler) 处理
let obj = {
	a:1
}
let {proxy, revoke} = Proxy.revocable(obj,{
	get(target,key,proxy){
		return Reflect.get(target,key,proxy);
	}
});
console.log(proxy.a);
revoke();
console.log(proxy.a); // 访问无效

这差不多就是Proxy的all content.

原文链接: https://www.villianhr.com/2016/07/29/超好用的es6 proxy

更新时间: 2016-07-29