- What: 定义一系列算法,把他们一个个封装起来,并且使他们可以相互替换(相互替换主要针对静态语言,对于js弱类型语言则不是很重要)
- where: 验证表单;运动函数;
- when:
- who:
- why: 优点:解决代码量、开放封闭(需要增加if else判断、缺乏弹性)(扩展性)、复用行性;缺点:必须了解所有策略才可以选择
- how: 将算法的使用和算法的实现分离出来(可以体现出多态性)
eq1: 基于策略模式的编写方式:
- 策略类:封装具体算法;
- 环境类:接受客户请求,把请求委托给某一个策略;
eq2: 箭头函数
- 箭头函数没有prototype属性;
- 没能作为构造器;
- 不能作为函数生成器;
eq3:不建议使用prototype和箭头函数结合,影响this指向问题
简单版实现
var performanceS = function() {};
performanceS.prototype.calculate = function(salary) {
return salary * 4;
};
var performanceA = function() {};
performanceA.prototype.calculate = function(salary) {
return salary * 3;
};
var performanceB = function() {};
performanceB.prototype.calculate = function(salary) {
return salary * 2;
};
var Bonus = function() {
this.salary = null;
this.strategy = null;
};
Bonus.prototype.setSalary = function(salary) {
this.salary = salary;
};
Bonus.prototype.setStrategy = function(strategy) {
this.strategy = strategy;
};
Bonus.prototype.getBonus = function() {
if (!this.strategy) {
throw new Error('strategy is undefined');
}
return this.strategy.calculate(this.salary);
};
var bonus = new Bonus();
bonus.setSalary(1000);
bonus.setStrategy(new performanceS());
console.log(bonus.getBonus());
bonus.setStrategy(new performanceB());
console.log(bonus.getBonus());
基于js语言
var strategies = {
'S': (salary) => 4 * salary,
'A': (salary) => 3 * salary,
'B': (salary) => 2 * salary
};
var calculateBonus = (level, salary) => {
return strategies[level] && strategies[level](salary);
};
console.log(calculateBonus('S', 1000));
console.log(calculateBonus('B', 1000));
应用实例:
表单验证
/**
* 表单校验
*/
// 没有解决非必传字段的校验
var strategies = {
isNonEmpty: (value, errorMsg) => {
if (value === '') {
return errorMsg;
}
},
minLength: (value, length, errorMsg) => {
if (value.length < length) {
return errorMsg;
}
},
isMobile: (value, errorMsg) => {
if (!/^1[3|5|8][0-9]{9}$/.test(value)) {
return errorMsg;
}
}
};
var Validate = function() {
this.cache = [];
};
Validate.prototype.add = function(value, 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(value); // 向数组头部插入
strategyAry.push(errorMsg); // 向数组尾部插图
return strategies[strategy].apply(value, strategyAry); // apply(this, [参数]) 对于不同策略参数个数不同,使用apply解决参数个数问题
});
})(rule);
}
};
Validate.prototype.start = function() {
for(var i = 0, validatorFunc; validatorFunc = this.cache[i++];) {
var msg = validatorFunc();
if (msg) {
return msg;
}
}
};
var registerForm = {
userName: 'xiaoming',
password: '123456',
phoneNumber: '13333333333'
};
var validateFunc = function() {
var validate = new Validate();
validate.add(registerForm.userName, [{
strategy: 'isNonEmpty',
errorMsg: 'userName is empty!'
}, {
strategy: 'minLength:10',
errorMsg: 'userName is less than 10!'
}]);
validate.add(registerForm.password, [{
strategy: 'minLength:6',
errorMsg: 'userName is less than 10!'
}]);
validate.add(registerForm.phoneNumber, [{
strategy: 'isMobile',
errorMsg: 'fill in phone number!'
}]);
var errorMsg = validate.start();
return errorMsg;
};
console.log(validateFunc());
运动
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
.box {
position: absolute;
background: blue;
width: 50px;
height: 50px;
left: 0;
top: 0;
}
</style>
</head>
<body>
<div class="box" id="box"></div>
</body>
<script>
var tween = {
Linear: (t, b, c, d) => c * t / d + b,
easeIn: (t, b, c, d) => c * (t /= d) * t + b,
strongEaseIn: (t, b, c, d) => c * (t /= d) * t * t * t * t + b,
strongEaseOut: (t, b, c, d) => c * ((t /= d - 1) * t * t * t * t + 1) + b,
sineaseIn: (t, b, c, d) => c * (t /= d) * t * t + b,
sineaseOut: (t, b, c, d) => c * ((t /= d - 1) * t * t + 1) + b
};
var Animate = function(dom) {
this.dom = dom;
this.startTime = 0;
this.startPos = 0;
this.endPos = 0;
this.propertyName = null;
this.easing = null;
this.duration = null;
};
Animate.prototype.start = function(propertyName, endPos, duration, easing) {
this.startTime = +new Date();
this.startPos = this.dom.getBoundingClientRect()[propertyName];
this.propertyName = propertyName;
this.endPos = endPos;
this.duration = duration;
this.easing = tween[easing];
var self = this;
var timeId = setInterval(function() {
if(self.step() === false) {
clearInterval(timeId);
}
}, 10);
};
Animate.prototype.step = function() {
var t = +new Date();
if (t >= this.startTime + this.duration) {
this.update(this.endPos);
return false;
}
var pos = this.easing(t-this.startTime, this.startPos, this.endPos-this.startPos, this.duration);
this.update(pos);
};
Animate.prototype.update = function(pos) {
this.dom.style[this.propertyName] = pos + 'px';
};
var dom = document.querySelector('#box');
var animate = new Animate(dom);
animate.start('left', 200, 500, 'sineaseOut');
</script>
</html>