【ES6】Proxy对象
- 一、Proxy的基本用法
- 二、Proxy示例的方法
- 1)get()
- 2)set()
- 3)apply()
- 查看更多ES6教学文章:
- 参考文献
引言:ES6规范里面新增了Proxy对象,在高级范畴的js编程或者底层脚本的编写有这极强的作用。 |
一、Proxy的基本用法
Proxy用于修改某些操作的默认行为,等同于在语言层面做出修改,所以属于一种“元编程"( meta programming),即对编程语言进行编程。
Proxy可以理解成在目标对象前架设一“拦截”层,外界对该对象的访问都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。Proxy 这个词的原意是代理,用在这里表示由它来“代理"”某些操作,可以译为“代理器”。
在介绍Proxy对象的基本用法之前,我们先来讲述一下JS中对象的机制。例如,我先简单的创建一个对象:var obj = {};
,现在obj
对象是没有属性的。假如我再写一条语句obj.id=1;
,此时,虽然obj对象没有id这条属性,但是obj.id=1
操作是调用了JS对象机制内部的set方法。
下面,给出Proxy对象定义的基本格式:var proxy = new Proxy(target , handler);
Proxy对象的所有用法都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成个Proxy实例,target参数表示所要拦截的目标对象的名称,hander参数是一个函数方法,用来定制拦截行为。
代码块1-1运用Proxy对象写了一个拦截对象对自己的属性进行赋值与取值的代理器。
/***@@ 代码块1-1 拦截器举例一 @@***/
var obj = new Proxy({},{
get: function (target, key, receiver) {
console. log(`getting ${key}!`);
return Reflect.get(target, key, receiver);
},
set: function (target, key, value, receiver) {
console. log(`setting ${key}!`);
return Reflect.set(target, key, value, receiver);
});
obj.count=1;
// setting count!
++obj.count;
//getting count!
//setting count!
//2
代码块1-1解释:
首先,定义obj对象为Proxy类。
Proxy第一个参数应该是一个对象名,表示要拦截的目标对象,本例为空,则指向源对象obj。
Proxy第二个参数是自定义的拦截方法。
本例是对JS对象机制的get与set进行重定义,其中target参数是目标对象名,key是属性名。
当obj.count=1;代码运行时,首先会调用obj对象的set函数机制,然后被Proxy拦截,输出setting count!。
当obj.count++;运行时,先get到count的值,再去set。
代码块1-1对一个空对象做了一层拦截,重定义了属性的读取(get)和设置(set)行为。Proxy 实际上重载(overlad)了点运算符,即用自己的定义覆盖了语言的盾始定义。
下面是另一个拦截读取属性行为的例子。见代码块1-2。
/***@@ 代码块1-2 拦截读取属性 @@***/
var proxy = new Proxy({}, {
get: function(target, property) {
return 35;
}
});
proxy.time // 35
proxy.nane // 35
proxy.title // 35
上面的代码中,作为构造函数Proxy接受两个参数:第一个参数是所要代理的目标对象(上例中是一个空对象),即如果没有Proxy介人,操作原来要访问的就是这个对象;第二个参数是配置对象,对于每一个被代理的操作,需要提供一 个对应的处理函数,该函数将拦截对应的操作。比如,上面的代码中, 配置对象有一个get方法,用来拦截对目标对象属性的访问请求。get方法的两个参数分别是目标对象和所要访问的属性。可以看到,由于拦截函数总是返回35,所以访问任何属性都将得到35。
注意,要使Proxy起作用,必须针对Proxy实例(上例中是proxy对象)进行操作,而不是针对目标对象(上例中是空对象)进行操作。
如果handler没有设置任何拦截,那就等同于直接通向原对象。见代码块1-3。
/***@@ 代码块1-3 @@***/
var target = {};
var handler = {};
var proxy = new Proxy(target, handler);
proxy.a = 'b';
target.a // "b"
上面的代码中,handler 是一个空对象,没有任何拦藏效果,访问hndler就等同于访问target。
一个技巧是将 Proxy对象设置到object.proxy属性,从而可以在object对象上调用。
var object= { proxy: new Proxy(target, handler) }
二、Proxy示例的方法
1)get()
get方法用于拦截某个属性的读取操作。前面已经有一个例子, 下面是另一个拦截读取操作的例子。见代码块2-1-1。
/***@@ 代码块2-1-1 @@***/
var person = {
name: "张三"
};
var proxy = new Proxy(person, {
get: function(target, property) {
if (property in target) {
return target[property];
} else {
throw new ReferenceError("Property \"" + property + "\" does not exist.");
}
}
});
proxy.name // "张三”
proxy.age //抛出一个错误
2)set()
set方法用于拦截某个属性的赋值操作。
假定Person对象有个age属性,该属性应该是个不大于200的整数,那么可以使用Porxy对象保证age的属性值符合要求。见代码块2-2-1。
/***@@ 代码块2-2-1 @@***/
let validator = {
set: function(obj, prop, value) {
if (prop === 'age') {
if (!Number.isInteger(value)) {
throw new TyeError('The age is not an integer');
}
if (value > 200) {
throw new RangeError('The age seems invalid');
}
}
//其他属性一律保存
obj[prop] = value;
}
};
let person = new Proxy({}, validator);
person.age = 100;
person.age // 100
person.age = 'young' //报错
person.age = 300 //报错
上面的代码中,由于设置了存值函数set,任何不符合要求的age属性赋值都会抛出一个错误。利用set方法,还可以数据绑定,即每当对象发生变化时,会自动更新DOM。
有时,我们会在对象上设置内部属性,属性名的第一个字符使用 下画线开头,表示这些属性不应该被外部使用。结合get和set方法,就可以做到防止这些内部属性被外部读写。
/***@@ 代码块2-2-1 屏蔽下划线开头的属性 @@***/
var handler = {
get (target, key) {
invartant(key, 'get');
return target[key];
},
set (target, key, value) {
invartant(key, 'set');
return true;
}
}
functton invartant (key, action) {
if (key[0] === '_') {
throw new Error(`Invalid attempt to ${action} private "${key}" property`);
}
}
var target = {};
var proxy = new Proxy(target, handler);
proxy._prop
//Error: Invalid attempt to get private " prop" property
proxy._prop = 'c'
// Error: Invalid attempt to set private " prop" property
上面的代码中,只要读写的属性名的第一个字符是下画线,一律抛出错误,从而达到禁止读/写内部属性的目的。
3)apply()
apply方法拦截函数的调用、call和apply操作。形式如下:
var handler = {
apply方法可以接受3个参数,分别是目标对象、目标对象的上下文对象( this )和目标对象的参数数组。下面是一个例子。
apply (target, ctx, args) {
return Reflect. apply(…arguments);}
}
var target= function () { return 'I am the target'; };
var handler =
apply: function () {
return 'I am the proxy';
};
var p = new Proxy(target, handler);
p() === 'I am the proxy';
// true
上面的代码中,变量p是Proxy的实例,当作为函数调用时(p()),就会被apply方法拦截,返回一个字符串。
查看更多ES6教学文章:
1. 【ES6】let与const 详解2. 【ES6】变量的解构赋值
3. 【ES6】字符串的拓展
4. 【ES6】正则表达式的拓展
5. 【ES6】数值的拓展
6. 【ES6】数组的拓展
7. 【ES6】函数的拓展
8. 【ES6】对象的拓展
9. 【ES6】JS第7种数据类型:Symbol
10. 【ES6】Proxy对象
11. 【ES6】JS的Set和Map数据结构
12. 【ES6】Generator函数详解
13. 【ES6】Promise对象详解
14. 【ES6】异步操作和async函数
15. 【ES6】JS类的用法class
16. 【ES6】Module模块详解
17. 【ES6】ES6编程规范 编程风格
参考文献
阮一峰 《ES6标准入门(第2版)》