ES6 Proxy 的思考

作者 johansen 日期 2017-03-16
ES6 Proxy 的思考
技术有时间时间长了不用,就会忘,有时间就得可以要求自己去适应新的开发模式,最近再次入手ES6,每次温习,总要有所收获,深入程度也需要有所增加。本周继续ES6引发的思考。

ES6’s Proxy

所谓Proxy,即在目标对象之前架设一层“拦截”,外界对该对象的访问,都必须先通过这层拦截,因此提供了一种机制,可以对外界的访问进行过滤和改写。这就像是代理。

ES6 提供 Proxy 构造函数,用来生成 Proxy 实例。

1
var proxy = new Proxy(target, handler);

Proxy 对象的所有用法,都是上面这种形式,不同的只是handler参数的写法。其中,new Proxy()表示生成一个Proxy实例,target参数表示所要拦截的目标对象,handler参数也是一个对象,用来定制拦截行为。

我把常用的几个函数(get,set,apply,construct)拎出来举个例子,其他的请参考ES6 Proxy

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
var handler = {
get: function(target, name) {
if (name === 'prototype') {
return Object.prototype;
}
return 'Hello, ' + name;
},
set: function(target, name){
console.log(`setting ${name}!`);
},
apply: function(target, thisBinding, args) {
return args[0];
},
construct: function(target, args) {
return {value: args[1]};
}
};
var fproxy = new Proxy(function(x, y) {
return x + y;
}, handler);
fproxy(1, 2) // 1
new fproxy(1,2) // {value: 2}
fproxy.prototype === Object.prototype // true
fproxy.foo // "Hello, foo"
fproxy.voo = 'v';// setting voo
  • 1.get方法用于拦截某个属性的读取操作。
  • 2.set方法拦截某个属性的赋值操作。
  • 3.apply方法拦截函数的调用、call和apply操作。apply方法可以接受三个参数,分别是目标对象、目标对象的上下文对象(this)和目标对象的参数数组。
  • 4.construct方法用于拦截new命令。construct方法可以接受两个参数。target目标对象,args构建函数的参数对象。注意他需要返回一个对象。

综述,ES6的proxy实现了对目标对象的访问之前的介入,这也就是一种隔离。对目标对象的访问增加了安全等级,对于目标对象实现很好的私有化与公有化的分离。(保护代理,单一职责原则)

由此我们可以引入关于JS设计模式的思考 –代理模式

JS 设计模式–代理模式

代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。

保护代理与虚拟代理

代理B可以帮助A过滤掉一些请求,比如送花的人中年龄太大的或者没有宝马的,这种请求就可以直接在代理B处被拒绝掉。这种代理叫作保护代理。

另外,假设现实中的花价格不菲,导致在程序世界里,买花也是一个代价昂贵的操作,那么我们可以把买花的操作交给代理B去执行,代理B会选择在A心情好时再执行买花,这是代理模式的另一种形式,叫作虚拟代理。虚拟代理把一些开销很大的对象,延迟到真正需要它的时候才去创建。

虚拟代理

利用虚拟代理实现图片懒加载

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
var myImage = (function(){
var imgNode = document.createElement( 'img' );
document.body.appendChild( imgNode );
return {
setSrc: function( src ){
imgNode.src = src;
}
}
})();
//代理
var proxyImage = (function(){
var img = new Image; //虚拟dom
img.onload = function(){
myImage.setSrc( this.src );//请求的图片加载完全后重写真实dom的src
}
return {
setSrc: function( src ){
myImage.setSrc( 'file:// /C:/Users/svenzeng/Desktop/loading.gif' );//默认图片占位
img.src = src;
}
}
})();
proxyImage.setSrc( 'http:// imgcache.qq.com/music/photo/k/000GGDys0yA0Nk.jpg' );

现在我们通过proxyImage间接地访问MyImage。proxyImage控制了客户对MyImage的访问,并且在此过程中加入一些额外的操作,比如在真正的图片加载好之前,先把img节点的src设置为一张本地的loading图片。

代理模式我们需要注意什么

  1. 单一职责原则:就一个类(通常也包括对象和函数等)而言,应该仅有一个引起它变化的原因。如果一个对象承担了多项职责,就意味着这个对象将变得巨大,引起它变化的原因可能会有多个。面向对象设计鼓励将行为分布到细粒度的对象之中,如果一个对象承担的职责过多,等于把这些职责耦合到了一起,这种耦合会导致脆弱和低内聚的设计。当变化发生时,设计可能会遭到意外的破坏。

  2. 代理和本体接口的一致性,当有一天不在使用代理,当使用本体时由于对外接口一致,对于替换迁移很有帮助。

综上思考,温故知新,必有所得。周末愉快,话说明天还得骑行100KM,早点睡了。