定義一系列算法,把它們封裝起來,並使它們能夠相互替換。具體來講就是,定義一系列算法,把它們各自封裝成策略類,算法被封裝在策略類內部的方法。在客戶對Context發起請求的時,Context老是把請求委託給策略對象中的某個方法計算。css
// 把策略類定義爲函數 var strategies = { "S": function( salary ){ return salary * 4; }, "A": function( salary ){ return salary * 3; }, "B": function( salary ){ return salary * 2; } }; //用函數充當Context var calculateBonus = function( level, salary ){ return strategies[ level ]( salary ); }; console.log( calculateBonus( 'S', 20000 ) ); // 輸出:80000 console.log( calculateBonus( 'A', 10000 ) ); // 輸出:30000
編寫一些動畫類和緩動算法,讓小球以各類緩動效果運動。算法
運動前至少要記錄的信息:小球原始位置、小球的目標位置、動畫開始的準確時間和小球運動的持續時間。app
用setInterval建立定時器,每一幀把動畫已消耗事件 、小球原始位置、小球目標位置和動畫持續事件傳入緩動算法。該算法能算出小球當前應所處位置,並更新該div的CSS屬性。dom
這些緩動算法來自Flash,分別接受4個參數:動畫已消耗的時間、小球原始位置、小球目標位置、動畫持續的總時間,返回的值是動畫元素應該所處的當前位置。函數
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; } };
var Animate = function( dom ){ this.dom = dom; // 進行運動的dom 節點 this.startTime = 0; // 動畫開始時間 this.startPos = 0; // 動畫開始時,dom 節點的位置,即dom 的初始位置 this.endPos = 0; // 動畫結束時,dom 節點的位置,即dom 的目標位置 this.propertyName = null; // dom 節點須要被改變的css 屬性名 this.easing = null; // 緩動算法 this.duration = null; // 動畫持續時間 }; //啓動動畫,記錄信息,並啓動定時器 Animate.prototype.start = function( propertyName, endPos, duration, easing ){ this.startTime = +new Date; // 動畫啓動時間 this.startPos = this.dom.getBoundingClientRect()[ propertyName ]; // dom 節點初始位置 this.propertyName = propertyName; // dom 節點須要被改變的CSS 屬性名 this.endPos = endPos; // dom 節點目標位置 this.duration = duration; // 動畫持續事件 this.easing = tween[ easing ]; // 緩動算法 var self = this; var timeId = setInterval(function(){ // 啓動定時器,開始執行動畫 if ( self.step() === false ){ // 若是動畫已結束,則清除定時器 clearInterval( timeId ); } }, 19 ); }; //表示小球每一幀要作的事:計算小球當前位置和調用CSS屬性值的方法 Animate.prototype.step = function(){ var t = +new Date; // 取得當前時間 if ( t >= this.startTime + this.duration ){ // (1) this.update( this.endPos ); // 更新小球的CSS 屬性值 return false; } var pos = this.easing( t - this.startTime, this.startPos, this.endPos - this.startPos, this.duration ); // pos 爲小球當前位置 this.update( pos ); // 更新小球的CSS 屬性值 }; //更新小球CSS屬性值 Animate.prototype.update = function( pos ){ this.dom.style[ this.propertyName ] = pos + 'px'; }; var div = document.getElementById( 'div' ); var animate = new Animate( div ); animate.start( 'left', 500, 1000, 'strongEaseOut' );
把校驗邏輯封裝成策略對象:動畫
var strategies = { isNonEmpty: function( value, errorMsg ){ // 不爲空 if ( value === '' ){ return errorMsg ; } }, minLength: function( value, length, errorMsg ){ // 限制最小長度 if ( value.length < length ){ return errorMsg; } }, isMobile: function( value, errorMsg ){ // 手機號碼格式 if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ return errorMsg; } } };
實現Validator類做爲Conext,接收用戶的請求並委託給strategy對象:this
var Validator = function(){ this.cache = []; // 保存校驗規則 }; Validator.prototype.add = function( dom, rule, errorMsg ){ var ary = rule.split( ':' ); // 把strategy 和length參數分開 this.cache.push(function(){ // 構建ary參數,把匿名函數放進cache var strategy = ary.shift(); // 用戶挑選的strategy ary.unshift( dom.value ); // 把input 的value 添加進參數length前 ary.push( errorMsg ); // 把errorMsg 添加進參數length後 return strategies[ strategy ].apply( dom, ary ); }); }; Validator.prototype.start = function(){ for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ var msg = validatorFunc(); // 開始校驗,並取得校驗後的返回信息 if ( msg ){ // 若是有確切的返回值,說明校驗沒有經過 return msg; } } };
建立validator對象,經過validator.add方向validator對象添加校驗規則。而後調用validator.start()方法啓動驗證。。若是validator.start()返回了確切的errorMsg字符串做爲返回值,說明驗證失敗。spa
var validataFunc = function(){ var validator = new Validator(); // 建立一個validator 對象 /***************添加一些校驗規則****************/ validator.add( registerForm.userName, 'isNonEmpty', '用戶名不能爲空' ); validator.add( registerForm.password, 'minLength:6', '密碼長度不能少於6 位' ); validator.add( registerForm.phoneNumber, 'isMobile', '手機號碼格式不正確' ); var errorMsg = validator.start(); // 得到校驗結果 return errorMsg; // 返回校驗結果 } var registerForm = document.getElementById( 'registerForm' ); registerForm.onsubmit = function(){ var errorMsg = validataFunc(); // 若是errorMsg 有確切的返回值,說明未經過校驗 if ( errorMsg ){ alert ( errorMsg ); return false; // 阻止表單提交 } };
/***********************策略對象**************************/ var strategies = { isNonEmpty: function( value, errorMsg ){ if ( value === '' ){ return errorMsg; } }, minLength: function( value, length, errorMsg ){ if ( value.length < length ){ return errorMsg; } }, isMobile: function( value, errorMsg ){ if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ return errorMsg; } } }; /***********************Validator 類**************************/ var Validator = function(){ this.cache = []; }; Validator.prototype.add = function( dom, rules ){ var self = this; for ( var i = 0, rule; rule = rules[ i++ ]; ){ (function( rule ){ var strategyAry = rule.strategy.split( ':' ); var errorMsg = rule.errorMsg; self.cache.push(function(){ var strategy = strategyAry.shift(); strategyAry.unshift( dom.value ); strategyAry.push( errorMsg ); return strategies[ strategy ].apply( dom, strategyAry ); }); })( rule ) } }; Validator.prototype.start = function(){ for ( var i = 0, validatorFunc; validatorFunc =this.cache[i++]; ){ var errorMsg = validatorFunc(); if ( errorMsg ){ return errorMsg; } } }; /***********************客戶調用代碼**************************/ var registerForm = document.getElementById( 'registerForm' ); var validataFunc = function(){ var validator = new Validator(); validator.add( registerForm.userName, [{ strategy: 'isNonEmpty', errorMsg: '用戶名不能爲空' }, { strategy: 'minLength:6', errorMsg: '用戶名長度不能小於10 位' }]); validator.add( registerForm.password, [{ strategy: 'minLength:8', errorMsg: '密碼長度不能小於6 位' }]); validator.add( registerForm.phoneNumber,[{ strategy: 'isMobile', errorMsg: '手機號碼格式不正確' }] ); var errorMsg = validator.start(); return errorMsg; } registerForm.onsubmit = function(){ var errorMsg = validataFunc(); if ( errorMsg ){ alert ( errorMsg ); return false; } };
策略模式的優缺點prototype
優勢:code
缺點