本系列文章主要根據《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)