本系列文章主要根据《JavaScript设计模式与开发实践》整理而来,其中会加入了一些本身的思考。但愿对你们有所帮助。javascript
js设计模式--单例模式html
js设计模式--策略模式java
js设计模式--代理模式es6
代理模式是为一个对象提供一个代用品或占位符,以便控制对它的访问。segmentfault
好比,明星都有经纪人做为代理。若是想请明星来办一场商业演出,只能联系他的经纪人。经纪人会把商业演出的细节和报酬都谈好以后,再把合同交给明星签。设计模式
于控制不一样权限的对象对目标对象的访问,如上面明星经纪人的例子
把一些开销很大的对象,延迟到真正须要它的时候才去建立。 如短期内发起不少个http请求,咱们能够用虚拟代理实现必定时间内的请求统一发送
1. 能够保护对象 2. 优化性能,减小开销很大的对象 3. 缓存结果
var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); return { setSrc: function (src) { imgNode.src = src; } } })(); myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
想象一下,若是咱们的图片很大,用户就会看到页面很长一段时间是空白
咱们能够想到的改进是图片加载完成以前都展现loading图片缓存
var myImage = (function () { var imgNode = document.createElement('img'); document.body.appendChild(imgNode); var img = new Image() img.onload = () => { // 模拟图片加载 setTimeout(() => { imgNode.src = img.src }, 1000) } return { setSrc: function (src) { img.src = src imgNode.src = 'https://content.igola.com/static/WEB/images/other/loading-searching.gif'; } } })(); myImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
这段代码违背了单一职责原则,这个对象同时承担了加载图片和预加载图片两个职责
同时也违背了开放封闭原则,若是咱们之后不须要预加载图片了,那咱们不得不修改整个对象app
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() img.onload = function() { myImage.setSrc(img.src) } return { setSrc: function (src) { img.src = src myImage.setSrc('https://content.igola.com/static/WEB/images/other/loading-searching.gif') } } })() proxyImage.setSrc('https://segmentfault.com/img/bVbmvnB?w=573&h=158');
注意:咱们的代理和本体接口要保持一致性,如上面proxyImage和myImage都返回一个包含setSrc方法的对象。居于这点咱们写代理的时候也有迹可循。性能
<body> <div id="wrapper"> <input type="checkbox" id="1"></input>1 <input type="checkbox" id="2"></input>2 <input type="checkbox" id="3"></input>3 <input type="checkbox" id="4"></input>4 <input type="checkbox" id="5"></input>5 <input type="checkbox" id="6"></input>6 <input type="checkbox" id="7"></input>7 <input type="checkbox" id="8"></input>8 <input type="checkbox" id="9"></input>9 </div> </body> <script type="text/javascript"> // 模拟http请求 var synchronousFile = function (id) { console.log('开始同步文件,id 为: ' + id); }; var inputs = document.getElementsByTagName('input') var wrapper = document.getElementById('wrapper') wrapper.onclick = function (e) { if (e.target.tagName === 'INPUT') { synchronousFile(e.target.id) } } </script>
缺点很明显:每点一次就发送一次http请求优化
<body> <div id="wrapper"> <input type="checkbox" id="1"></input>1 <input type="checkbox" id="2"></input>2 <input type="checkbox" id="3"></input>3 <input type="checkbox" id="4"></input>4 <input type="checkbox" id="5"></input>5 <input type="checkbox" id="6"></input>6 <input type="checkbox" id="7"></input>7 <input type="checkbox" id="8"></input>8 <input type="checkbox" id="9"></input>9 </div> </body> <script type="text/javascript"> // 模拟http请求 var synchronousFile = function (id) { console.log('开始同步文件,id 为: ' + id); }; var inputs = document.getElementsByTagName('input') var wrapper = document.getElementById('wrapper') wrapper.onclick = function (e) { if (e.target.tagName === 'INPUT' && e.target.checked) { proxySynchronousFile(e.target.id) } } var proxySynchronousFile = (function () { var cacheIds = [], timeId = 0 return function (id) { if (cacheIds.indexOf(id) < 0) { cacheIds.push(id) } clearTimeout(timeId) timeId = setTimeout(() => { synchronousFile(cacheIds.join(',')) cacheIds = [] }, 1000) } })() </script>
var mult = function () { console.log('开始计算乘积'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; mult(2, 3); // 输出:6 mult(2, 3, 4); // 输出:24
var mult = function () { console.log('开始计算乘积'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; // mult(2, 3); // 输出:6 // mult(2, 3, 4); // 输出:24 var proxyMult = (function() { var cache = {} return function () { let id = Array.prototype.join.call(arguments, ',') if (cache[id]) { return cache[id] } else { return cache[id] = mult.apply(this, arguments) } } })() proxyMult(2, 3); // 输出:6 proxyMult(2, 3); // 输出:6
咱们如今但愿加法也可以缓存
var mult = function () { console.log('开始计算乘积'); var a = 1; for (var i = 0, l = arguments.length; i < l; i++) { a = a * arguments[i]; } return a; }; var plus = function () { console.log('开始计算和'); var a = 0; for (var i = 0, l = arguments.length; i < l; i++) { a = a + arguments[i]; } return a; }; // mult(2, 3); // 输出:6 // mult(2, 3, 4); // 输出:24 var createProxyFactory = function (fn) { var cache = {} return function () { let id = Array.prototype.join.call(arguments, ',') if (cache[id]) { return cache[id] } else { return cache[id] = fn.apply(this, arguments) } } } var proxyMult = createProxyFactory(mult), proxyPlus = createProxyFactory(plus); proxyMult(1, 2, 3, 4) // 输出:24 proxyMult(1, 2, 3, 4) // 输出:24 proxyPlus(1, 2, 3, 4) // 输出:10 proxyPlus(1, 2, 3, 4) // 输出:10
class Car { drive() { return "driving"; }; } class CarProxy { constructor(driver) { this.driver = driver; } drive() { return ( this.driver.age < 18) ? "too young to drive" : new Car().drive(); }; } class Driver { constructor(age) { this.age = age; } }
// 明星 let star = { name: '张XX', age: 25, phone: '13910733521' } // 经纪人 let agent = new Proxy(star, { get: function (target, key) { if (key === 'phone') { // 返回经纪人本身的手机号 return '18611112222' } if (key === 'price') { // 明星不报价,经纪人报价 return 120000 } return target[key] }, set: function (target, key, val) { if (key === 'customPrice') { if (val < 100000) { // 最低 10w throw new Error('价格过低') } else { target[key] = val return true } } } }) // 主办方 console.log(agent.name) console.log(agent.age) console.log(agent.phone) console.log(agent.price) // 想本身提供报价(砍价,或者高价争抢) agent.customPrice = 150000 // agent.customPrice = 90000 // 报错:价格过低 console.log('customPrice', agent.customPrice)