为其他对象提供一种代理以控制对这个对象的访问
特点如下
- 代理对象可预先处理请求,再决定是否转交给本体;
- 代理和本体对外显示接口保持一致性
- 代理对象仅对本体做一次包装
1. 模式细分
- 虚拟代理(将开销大的运算交给代理,延迟到需要时执行)
- 缓存代理(为开销大的运算结果提供缓存)
- 保护代理(用于对象应该有不同的访问权限)
- 防火墙代理(控制网络资源的访问)
- 远程代理(为一个对象在不同的地址控件提供局部代表)
- 智能引用代理(访问对象执行一些附加操作)
- 写时复制代理(延迟对象复制过程,对象需要真正修改时才进行)
2. 虚拟代理实现图片预加载
在所需图片未加载前,用loading图片占位,然后用异步的方式加载图片,等图片加载好了,替换掉loading,下面先不用代理模式实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| let MyImage = (function() { let imgNode = document.createElement('img'); document.body.appendChild(imgNode); let img = new Image; img.onload = function () { imgNode.src = img.src; }; return { setSrc: function (src) { imgNode.src = "file://loading.gif"; img.src = src; } } })
|
违背了单一职责原则,在这种高耦合的情况下,如果不需要loading效果,那么只能在MyImage
下动刀。
接下来我们用虚拟代理重构一下这段代码
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
| let myImage = (function () { let imgNode = document.createElement('img'); document.body.appendChild(imgNode);
return { setSrc: function (src) { imgNode.src = src; } } })();
let proxyImage = (function () { let img = new Image; img.onload = function () { myImage.setSrc(this.src); }; return { setSrc: function (src) { myImage.setSrc("file://loading.gif"); img.src = src; } } })(); proxyImage.setSrc( 'http://www.lll.com/photo/true.jpg' );
|
这样就解耦了
3. 虚拟代理在惰性加载中的应用
实现一个等用户按F2
唤出控制台的时候,再开始加载miniConsole.js代码,并在加载完成后执行cache
中的log函数
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
| let miniConsole = (function () { let cache = []; let handler = function (e) { if (e.keyCode === 113) { let script = document.createElement("script"); script.onload = function () { for (let i = 0, fn; fn = cache[i++];) { fn(); } }; script.src = "miniConsole.js"; document.getElementsByClassName("head")[0].appendChild(script); document.body.removeEventListener("keydown", handler); } } document.body.addEventListener("keydown", handler, false); return { log: function () { let args = arguments; cache.push(function () { return miniConsole().log.apply(miniConsole().args); }) } } })();
miniConsole.log(11);
|
4. 缓存代理计算乘积
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| let mult = function () { let a = 1; for (let i = 0; i < arguments.length; i++) { a *= arguments[i]; } return a; }
let proxyMult = (function () { let cache = {}; return function () { let args = Array.prototype.join.call(arguments, ','); if (args in cache) { return cache[args]; } return cache[args] = mult.apply(this, arguments); } })();
proxyMult(1, 2, 3) proxyMult(1, 2, 3)
|
5. ES6中的Proxy
ES6 的 Proxy语法:let proxyObj = new Proxy(target, handler);
- target: 本体,要代理的对象
- handler: 自定义操作方法集合
- proxyObj: 返回的代理对象,拥有本体的方法,不过会被
handler
预处理
可以拦截并控制对对象的访问,下面是用Proxy实现的代理工厂
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const getCacheProxy = (fn, cache = new Map()) => { return new Proxy(fn, { apply(target, context, args) { const argsString = args.join(','); if (cache.has(argsString)) { return cache.get(argsString); } const result = fn(...args); cache.set(argsString, result); return result; } }) }
|
参考
《javascript设计模式与开发实践》
https://segmentfault.com/a/1190000019574843