1.模拟传统面向对象语言的状态模式实现
class Stopwatch {
constructor() {
this.button1 = null;
this.button2 = null;
this.resetState = new ResetState(this);
this.startState = new StartState(this);
this.pauseState = new PauseState(this);
this.currState = this.resetState;
}
init() {
this.dom = document.createElement('div');
this.dom.setAttribute('id', 'stopwatch');
this.dom.innerHTML = `
<div class="header">stopwatch</div>
<div class="main">
<!-- 时间显示部分 -->
<div class="display" >00:00.00</div>
<!-- 控制部分 -->
<div class="ctrl">
<button type="button" id='btnOne' disabled='disabled'>计次</button> <!--class="active"-->
<button type="button" id='btnTwo'>启动</button> <!--class="stop"-->
</div>
<!-- 显示计次(时间间隔) -->
<ol class="lap">
</ol>
</div>`;
document.body.appendChild(this.dom);
this.button1 = this.dom.querySelector('#btnOne');
this.button2 = this.dom.querySelector('#btnTwo');
this.display = this.dom.querySelector('.display');
this.lap = this.dom.querySelector('.lap');
this.bindEvent();
Stopwatch.addStopwatchListener(t => {
this.displayTotalTime(t);
});
Stopwatch.addStopwatchListener(t => {
this.displayLapTime(t);
})
}
bindEvent() {
this.button1.addEventListener('click', () => {
this.currState.clickHandler1();
}, false);
this.button2.addEventListener('click', () => {
this.currState.clickHandler2();
}, false);
}
start() {
if(timer.count === 0) {
timer.count = 1;
this.insertLap();
}
timer.startTimer();
this.setState(this.startState);
this.setStyle();
}
pause() {
timer.stopTimer();
this.setState(this.pauseState);
this.setStyle();
}
lapf() {
this.insertLap();
timer.timeAccumulationContainer.push(timer.timeAccumulation);
timer.s += timer.timeAccumulationContainer[timer.count - 1];
timer.timeAccumulation = 0;
timer.count++;
}
reset() {
timer.reset();
this.setState(this.resetState);
this.setStyle();
}
displayTotalTime() {
let totaltimeAccumulation = timer.timeAccumulation * 10 + timer.s * 10;
this.display.innerHTML = `${Timer.milSecond_to_time(totaltimeAccumulation)}`;
}
displayLapTime() {
let li = this.lap.querySelector('li'),
spans = li.querySelectorAll('span'),
task = spans[0], time = spans[1];
task.innerHTML = `计次${timer.count}`;
time.innerHTML = `${Timer.milSecond_to_time(timer.timeAccumulation * 10)}`;
}
setState(newState) {
this.currState = newState;
}
setStyle() {
if(this.currState instanceof StartState) {
let button1 = this.button1;
button1.disabled = '';
button1.innerHTML = '计次';
button1.className = 'active';
let button2 = this.button2;
button2.innerHTML = '停止';
button2.className = 'stop';
} else if (this.currState instanceof PauseState) {
this.button1.innerHTML = '复位';
let button2 = this.button2;
button2.innerHTML = '启动';
button2.className = 'start';
} else if (this.currState instanceof ResetState) {
let button1 = this.button1;
button1.disabled = 'disabled';
button1.innerHTML = '计次';
button1.className = '';
this.display.innerHTML = '00:00.00';
this.lap.innerHTML = '';
}
}
insertLap() {
let t = Stopwatch.templateLap();
this.lap.insertAdjacentHTML('afterbegin', t);
}
static templateLap() {
return `
<li><span></span><span></span></li>
`;
}
static addStopwatchListener(handler) {
timer.stopwatchHandlers.push(handler);
}
}
class State{
constructor() {}
static clickHandler1() {
throw new Error('父类的clickHandler1方法必须被重写');
}
static clickHandler2() {
throw new Error('父类的clickHandler2方法必须被重写');
}
}
class ResetState {
constructor(stopwatchObj) {
this.stopwatchObj = stopwatchObj;
}
static clickHandler1() {
console.log('初始状态下点击计次无效');
}
clickHandler2() {
console.log('初始状态下点击启动, 切换为启动状态');
this.stopwatchObj.start();
}
}
ResetState.prototype = new State();
class StartState {
constructor(stopwatchObj) {
this.stopwatchObj = stopwatchObj;
}
clickHandler1() {
console.log('启动状态下点击计次');
this.stopwatchObj.lapf();
}
clickHandler2() {
console.log('启动状态下点击暂停, 切换为暂停状态');
this.stopwatchObj.pause();
}
}
StartState.prototype = new State();
class PauseState {
constructor(stopwatchObj) {
this.stopwatchObj = stopwatchObj;
}
clickHandler1() {
console.log('暂停状态下点击复位, 切换为初始状态');
this.stopwatchObj.reset();
}
clickHandler2() {
console.log('暂停状态下点击启动, 切换为启动状态');
this.stopwatchObj.start();
}
}
PauseState.prototype = new State();
class Timer {
constructor() {
this.stopwathchTimer = null;
this.count = 0;
this.timeAccumulation = 0;
this.timeAccumulationContainer = [];
this.s = 0;
this.stopwatchHandlers = [];
}
reset() {
this.stopwathchTimer = null;
this.count = 0;
this.timeAccumulation = 0;
this.timeAccumulationContainer = [];
this.s = 0;
}
startTimer() {
this.stopTimer();
this.stopwathchTimer = setInterval(() => {
this.timeAccumulation++;
this.stopwatchHandlers.forEach(handler => {
handler(this.timeAccumulation);
})
}, 1000 / 100)
}
stopTimer() {
clearInterval(this.stopwathchTimer );
}
static milSecond_to_time(t) {
let time,
minute = Timer.addZero(Math.floor(t / 60000) % 60),
second = Timer.addZero(Math.floor(t / 1000) % 60),
centisecond = Timer.addZero(Math.floor(t / 10) % 100) ;
time = `${minute}:${second}.${centisecond}`;
return time;
}
static addZero(t) {
t = t < 10 ? '0' + t : t;
return t;
}
}
const timer = new Timer();
const stopwatchObj = new Stopwatch();
stopwatchObj.init();
2.JavaScript 版本的状态机
class Stopwatch {
constructor() {
this.button1 = null;
this.button2 = null;
this.FSM = {
reset: {
clickHandler1() {
},
clickHandler2() {
console.log('初始状态下点击启动, 切换为启动状态');
if(timer.count === 0) {
timer.count = 1;
this.insertLap();
}
timer.startTimer();
this.currState = this.FSM.start;
this.setStyle('startState');
}
},
start: {
clickHandler1() {
console.log('启动状态下点击计次, 不切换状态');
this.insertLap();
timer.timeAccumulationContainer.push(timer.timeAccumulation);
timer.s += timer.timeAccumulationContainer[timer.count - 1];
timer.timeAccumulation = 0;
timer.count++;
},
clickHandler2() {
console.log('启动状态下点击暂停, 切换为暂停状态');
timer.stopTimer();
this.currState = this.FSM.pause;
this.setStyle('pauseState');
}
},
pause: {
clickHandler1() {
console.log('暂停状态下点击复位, 切换为初始状态');
timer.reset();
this.currState = this.FSM.reset;
this.setStyle('resetState');
},
clickHandler2() {
console.log('暂停状态下点击启动, 切换为启动状态');
timer.startTimer();
this.currState = this.FSM.start;
this.setStyle('startState');
}
}
};
this.currState = this.FSM.reset;
}
init() {
this.dom = document.createElement('div');
this.dom.setAttribute('id', 'stopwatch');
this.dom.innerHTML = `
<div class="header">stopwatch</div>
<div class="main">
<!-- 时间显示部分 -->
<div class="display" >00:00.00</div>
<!-- 控制部分 -->
<div class="ctrl">
<button type="button" id='btnOne' disabled='disabled'>计次</button> <!--class="active"-->
<button type="button" id='btnTwo'>启动</button> <!--class="stop"-->
</div>
<!-- 显示计次(时间间隔) -->
<ol class="lap">
</ol>
</div>`;
document.body.appendChild(this.dom);
this.button1 = this.dom.querySelector('#btnOne');
this.button2 = this.dom.querySelector('#btnTwo');
this.display = this.dom.querySelector('.display');
this.lap = this.dom.querySelector('.lap');
this.bindEvent();
Stopwatch.addStopwatchListener(t => {
this.displayTotalTime(t);
});
Stopwatch.addStopwatchListener(t => {
this.displayLapTime(t);
})
}
bindEvent() {
this.button1.addEventListener('click', () => {
this.currState.clickHandler1.call(this);
}, false);
this.button2.addEventListener('click', () => {
this.currState.clickHandler2.call(this);
}, false);
}
displayTotalTime() {
let totaltimeAccumulation = timer.timeAccumulation * 10 + timer.s * 10;
this.display.innerHTML = `${Timer.milSecond_to_time(totaltimeAccumulation)}`;
}
displayLapTime() {
let li = this.lap.querySelector('li'),
spans = li.querySelectorAll('span'),
task = spans[0], time = spans[1];
task.innerHTML = `计次${timer.count}`;
time.innerHTML = `${Timer.milSecond_to_time(timer.timeAccumulation * 10)}`;
}
setStyle(newState) {
if(newState === 'startState') {
let button1 = this.button1;
button1.disabled = '';
button1.innerHTML = '计次';
button1.className = 'active';
let button2 = this.button2;
button2.innerHTML = '停止';
button2.className = 'stop';
} else if (newState === 'pauseState') {
this.button1.innerHTML = '复位';
let button2 = this.button2;
button2.innerHTML = '启动';
button2.className = 'start';
} else if (newState === 'resetState') {
let button1 = this.button1;
button1.disabled = 'disabled';
button1.innerHTML = '计次';
button1.className = '';
this.display.innerHTML = '00:00.00';
this.lap.innerHTML = '';
}
}
insertLap() {
let t = Stopwatch.templateLap();
this.lap.insertAdjacentHTML('afterbegin', t);
}
static templateLap() {
return `
<li><span></span><span></span></li>
`;
}
static addStopwatchListener(handler) {
timer.stopwatchHandlers.push(handler);
}
}
class Timer {
constructor() {
this.stopwathchTimer = null;
this.count = 0;
this.timeAccumulation = 0;
this.timeAccumulationContainer = [];
this.s = 0;
this.stopwatchHandlers = [];
}
reset() {
this.stopwathchTimer = null;
this.count = 0;
this.timeAccumulation = 0;
this.timeAccumulationContainer = [];
this.s = 0;
}
startTimer() {
this.stopTimer();
this.stopwathchTimer = setInterval(() => {
this.timeAccumulation++;
this.stopwatchHandlers.forEach(handler => {
handler(this.timeAccumulation);
})
}, 1000 / 100)
}
stopTimer() {
clearInterval(this.stopwathchTimer );
}
static milSecond_to_time(t) {
let time,
minute = Timer.addZero(Math.floor(t / 60000) % 60),
second = Timer.addZero(Math.floor(t / 1000) % 60),
centisecond = Timer.addZero(Math.floor(t / 10) % 100) ;
time = `${minute}:${second}.${centisecond}`;
return time;
}
static addZero(t) {
t = t < 10 ? '0' + t : t;
return t;
}
}
const timer = new Timer();
const stopwatchObj = new Stopwatch();
stopwatchObj.init();