ES6-Proxy

语法

proxy的作用是代理对象的某些行为,其 实质 是重载对象的方法。

举个例子,我们想要读取一个对象的某个属性

1
2
let obj = {a: 'a' , b: 'b'};
console.log(obj.a); // 'a'

而使用了proxy之后:

1
2
3
4
5
6
7
8
9
let obj = {a: 'a', b: 'b'};
let handler = {
get: fucntion(target, propoty){
return 35;
}
}
var proxy = new Proxy(obj, handler);
console.log(proxy.a);// 35

相当于 proxy通过handler里面的指令,代理了obj的行为。

proxy能代理的,并不仅仅是get函数,还有如下列表,具体的可以网上查阅下文档。

  • get
  • set
  • has
  • deleteProperty
  • enumerate
  • hasOwn
  • ownKeys
  • getOwnPropertyDescritor
  • defineProperty
  • preventExtensions
  • getPrototypeOf
  • isExtensible
  • setPrototypeOf
  • apply
  • construct

使用场景

看完了proxy的文档,感觉模模糊糊有了印象,但是还是对它的使用场景没有概念,网上搜索了下,发下一篇非常好的介绍使用场景的文章,地址如下: [译] 实例解析 ES6 Proxy 使用场景,下面我也会使用这篇文章的例子,来介绍proxy到底怎么用。

校验模块

这里是重写set函数,实现校验功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
let numericDataStore = {
count: 0,
amount: 1234,
total: 14
};
numericDataStore = new Proxy(numericDataStore, {
set(target, key, value, proxy) {
if (typeof value !== 'number') {
throw Error("Properties in numericDataStore can only be numbers");
}
return Reflect.set(target, key, value, proxy);
}
});
// 抛出错误,因为 "foo" 不是数值
numericDataStore.count = "foo";
// 赋值成功
numericDataStore.count = 333;

私有属性

js中的私有属性,约定俗成是通过前面加下划线的方式来实现,这里可以用proxy来实现,并且做到了强制私有

1
2
3
4
5
6
7
8
9
10
11
12
13
14
var api = {
_apiKey: '123abc456def',
/* mock methods that use this._apiKey */
getUsers: function(){},
getUser: function(userId){},
setUser: function(userId, config){}
};
// logs '123abc456def';
console.log("An apiKey we want to keep private", api._apiKey);
// get and mutate _apiKeys as desired
var apiKey = api._apiKey;
api._apiKey = '987654321';

访问日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
let api = {
_apiKey: '123abc456def',
getUsers: function() { /* ... */ },
getUser: function(userId) { /* ... */ },
setUser: function(userId, config) { /* ... */ }
};
function logMethodAsync(timestamp, method) {
setTimeout(function() {
console.log(`${timestamp} - Logging ${method} request asynchronously.`);
}, 0)
}
api = new Proxy(api, {
get: function(target, key, proxy) {
var value = target[key];
return function(...arguments) {
logMethodAsync(new Date(), key);
return Reflect.apply(value, target, arguments);
};
}
});
api.getUsers();

预警和拦截

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
let dataStore = {
noDelete: 1235,
oldMethod: function() {/*...*/ },
doNotChange: "tried and true"
};
const NODELETE = ['noDelete'];
const NOCHANGE = ['doNotChange'];
const DEPRECATED = ['oldMethod'];
dataStore = new Proxy(dataStore, {
set(target, key, value, proxy) {
if (NOCHANGE.includes(key)) {
throw Error(`Error! ${key} is immutable.`);
}
return Reflect.set(target, key, value, proxy);
},
deleteProperty(target, key) {
if (NODELETE.includes(key)) {
throw Error(`Error! ${key} cannot be deleted.`);
}
return Reflect.deleteProperty(target, key);
},
get(target, key, proxy) {
if (DEPRECATED.includes(key)) {
console.warn(`Warning! ${key} is deprecated.`);
}
var val = target[key];
return typeof val === 'function' ?
function(...args) {
Reflect.apply(target[key], target, args);
} :
val;
}
});
// these will throw errors or log warnings, respectively
dataStore.doNotChange = "foo";
delete dataStore.noDelete;
dataStore.oldMethod();

过滤操作

通过特征来过滤不必要的操作,例如下载中,解析中等中间过程,减少响应的压力

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let obj = {
getGiantFile: function(fileId) {/*...*/ }
};
obj = new Proxy(obj, {
get(target, key, proxy) {
return function(...args) {
const id = args[0];
let isEnroute = checkEnroute(id);
let isDownloading = checkStatus(id);
let cached = getCached(id);
if (isEnroute || isDownloading) {
return false;
}
if (cached) {
return cached;
}
return Reflect.apply(target[key], target, args);
}
}
});