我儘可能用最少的文字,最少的篇幅,講明白設計模式的方方面面。
全文鏈接javascript
把 算法 和 調用算法 的部分作拆分開
我舉個例子吧:你想要去三亞旅遊,途徑有不少種:飛機,火車,自駕遊。這幾種方法均可以到達目的地,可是過程是有所不一樣的。css
每一種選擇,都是一種策略。
在程序中策略的意義在於,把處理不一樣任務但目的相同的代碼整合在一塊兒。再用一層函數委託他們來處理具體的算法。這樣能夠消除本來程序中大片的條件分支語句html
上代碼: 策略模式(計算不一樣的旅行方式到達時間)java
// 定義策略 var strategies = { 'plane': function(distance) { return distance * 1; // 假設飛機的速度最快 }, 'train': function(distance) { return distance * 4; // 飛機的速度是火車的4倍 }, 'roadTrip': function(distance) { return distance * 10; // 飛機的速度是自駕遊的10倍 }, } // Context var calculateBonus = function(mode, distance) { if (typeof strategies[mode] === 'function') { return strategies[mode](distance); } return -1; } // 調用策略 console.log(calculateBonus('plane', 1000)); console.log(calculateBonus('train', 1000)); console.log(calculateBonus('roadTrip', 1000));
策略模式是比較好理解的,我們先看一段即將被策略模式改造的代碼
var calculateBonus = function(mode, distance) { if (mode === 'plane') { return distance * 1; } else if (mode === 'train') { return distance * 4; } else if (mode === 'roadTrip') { return distance * 10; } return -1; }
這段代碼最大的問題是, 代碼可複製性差, 不利於維護。每次有新的改動都必須扒開代碼,找到具體的某個函數去修改。效率低,容易引起連貫性錯誤。css3
爲了更加明確策略模式的使用場景,咱們一塊兒來實現一個動畫函數。
js 動畫原理改變 dom 的 css 屬性,好比 left, top, background-position。因此至少要提供一下一些信息。web
而後配合定時器 setInterval 在定時器中每一個 19 毫秒改變一次 dom 的 css 屬性,每次修改 dom 時把上面的4個參數傳給算法。算法會計算出當前應該所在的位置。最後在更新 dom 的 css 屬性。這樣動畫就完成了。算法
算法部分,這裏最初來自 Flash 但如今 css3 部分也是這樣實現的設計模式
在線體驗bash
// 先定義動畫緩動算法 var tween = { linear: function(t, b, c, d) { return c * t / d + b; }, easeIn: function(t, b, c, d) { return c * (t /= d) * t + b; }, strongEaseIn: function(t, b, c, d) { return c * (t /= d) * t * t * t * t + b; }, strongEaseOut: function(t, b, c, d) { return c * ((t = t / d - 1) * t * t * t * t + 1) + b; }, sineaseIn: function(t, b, c, d) { return c * (t /= d) * t * t + b; }, sineaseOut: function(t, b, c, d) { return c * ((t = t / d - 1) * t * t + 1) + b; } }
而後在 body 中添加入下節點dom
<div style="position: absolute; background:yellow;">im div</div>
接下來定義動畫類
let Animate = function(dom) { this.dom = dom; this.startTime = 0; this.startPos = 0; this.endPos = 0; this.propertyName = null; this.esing = null; this.duratin = null; } Animate.prototype.start = function(propertyName, endPos, duratin, esing) { this.startTime = Date.now(); this.startPos = this.dom.getBoundingClientRect()[propertyName]; this.propertyName = propertyName; this.endPos = endPos; this.duratin = duratin; this.esing = tween[esing]; let self = this; let timeId = setInterval(function() { if (self.step() === false) { clearInterval(timeId) } }, 19) } Animate.prototype.step = function() { var t = Date.now(); if (t >= this.startTime + this.duratin) { this.update(this.endPos) return false } var pos = this.esing( t - this.startTime, // 時間 this.startPos, // 開始值 this.endPos - this.startPos, // 運動距離 this.duratin // 總耗時 ) this.update(pos); } Animate.prototype.update = function(pos) { this.dom.style[this.propertyName] = pos + 'px'; }
來測試一下!
var div = document.getElementsByTagName('div')[0]; var animate = new Animate(div); animate.start('left', 500, 1000, 'linear') // animate.start('left', 500, 1000, 'easeIn') // animate.start('left', 500, 1000, 'strongEaseIn') // animate.start('left', 500, 1000, 'strongEaseOut') // animate.start('left', 500, 1000, 'sineaseIn') // animate.start('left', 500, 1000, 'sineaseOut')