皮卡丘js的重构

338 阅读4分钟

重构

在开始之前我们需要理解一个概念重构是什么?

重构:即不改变程序的输入输出即保证现有功能的情况下,对内部实现进行优化和调整。

ok,现在我们理解了重构的作用,那请看下面的这段代码

const run = () =>{
    n+=1;
    if(n>string.length){
        window.clearInterval(id);
        return
    }
}
省略代码
let id = setInterval(()=>{
    run();
})
const btnPause = document.quertSelector('#btnPause');
btnPause.onclick = () = {
    window.cleanInterval(id);
}
const btnPlay = document.querySelector('#btnPlay');
btnPlay.onclick = () =>{
    id = setInterval(()=>{
        run()
    },0)
}
const btnSlow = document.querySelector('#btnSlow');
btnSlow.onclick = () = >{
    window.cleanInterval(id);
    time = 300;
    id = setInterval(()=>{
        run();
    },time)
}

const btnNormal = document.querySelector('#btnNormal');
btnNormal.onclick = () = >{
    window.cleanInterval(id);
    time = 100;
    id = setInterval(()=>{
        run();
    },time)
}

const btnFast = document.querySelector('#btnFast');
btnFast.onclick = () = >{
    window.cleanInterval(id);
    time = 0;
    id = setInterval(()=>{
        run();
    },time)
}

上面这段代码的意思是当我们点击页面上的五个按钮时会执行不同的功能,暂停,播放,慢速,中速,快速这个五个功能。

现在再来看这段代码,你会发现功能是实现了,但是有大量的重复。重复的代码=垃圾的代码。那么有没有办法让上面的代码变得短小精悍?当然可以,我们可以把重复的代码提出来变成函数。

先举一个例子

const x = () => {
    run()
}

请问上面代码中的函数x是否和run等价?答案是等价的。为什么?调用x跑的是run的效果,run跑的也是run的效果,所以是等价的。那么在上面代码我们就可以简化了。这里如果不是太清楚的话我们可以记住一个结论setInterval里第一个参数不能加括号,除非返回的是一个函数。

id = setInterval(run,time)

ok完成到这里,代码看起来精简了许多,但是还是有重复的,重复就等于垃圾,让我们再一次把代码精简.

const play = () =>{
    retun setInterval(run,time);
}

btnNormal.onclick = () = >{
    window.cleanInterval(id);
    time = 100;
    id = play();
}

简化了之后,代码的逻辑就非常好懂了。play就是播放,点击播放之后把结果返回给id就ok。这个时候再看代码,发现window.clearInterval(id)也是重复的,那我们也可以精简了。

const pause = () =>{
    window.clearInterva(id)
})

btnNormal.onclick = () = >{
    pause();
    time = 100;
    id = play();
}

到目前位置代码算是重构完成了,我们还能够进一步的优化吗?当然可以,既然播放和暂停都能写成函数,那么慢速,中速,快速当然也可以。

const slow = () => {
    pause();
    time = 300;
    id = play();
}

btnSlow.onclick = slow;

上述代码中我们使用了一个概念,等价交换。在一个函数里面只是调用了另一个函数,什么都没做那么我们就可以这样写xxx.onclick = xxz.

现在代码看起来算是优化的差不多了,是否还能优化呢?当然可以,我们使用面向对象的方式进行优化。

const demo = document.querySelector('#demo');
const demo2 = document.querySelector('#dem2');
let n = 1;
demo.innerText = string.substr(0,n);
demo2.innerHTML = string.substr(0,n);
let time = 100;
const player = {
    run : ()=>{
        n+=1;
        if(n>string.length){
            window.clearInterval(id);
            return;
        }
        demo.innerText = string.substr(0,n);
        demo2.innerHTML = string.substr(0,n);
    },
    play:()=>{
        id = setInterval(player.run,time);
    },
    pause:()=>{
        widow.cleanInterval(id)
    },
    slow:()=>{
        player.pause();
        time=300;
        player.play();
    },
    normal:()=>{
        player.pause();
        time=100;
        player.play();
    },
    fast:()=>{
        player.pause();
        time=0;
        player.play();
    }
}
let id = player.play();
document.querySelector('#btnPlay').onclick = player.play
document.querySelector('#btnPause').onclick =player.pause
document.querySelector('#btnSlow').onclick =player.slow
document.querySelector('#btnNormal').onclick = player.normal
document.querySelector('#btnFast').onclick =player.fast

ok这就使用面向对象的方式完成了重构,到这一步我们还没有优化完成,一般在对象中我们需要有一个初始化方法把上面的n啊time啊什么的给弄进来。

let id;
init:()=>{
demo.innerText = string.substr(0,n);
demo2.innerHTML = string.substr(0,n);
player.play();
}
player.init();

这样弄完了之后,代码会变得更精简。在内部只有play的属性在外部只有几个变量,通过暴露init来实现皮卡丘。那么我们还可以更简化呢?可以,在对象里面不仅有初始化方法还有绑定方法,那么我们就可以把绑定的事件给扔进去,在init中调用即可。

init:()=>{
player.play();
player.bindEvents();
}
bindEvents:(){
document.querySelector('#btnPlay').onclick = player.play
document.querySelector('#btnPause').onclick =
document.querySelector('#btnSlow').onclick =player.slow
document.querySelector('#btnNormal').onclick = player.normal
document.querySelector('#btnFast').onclick
}
player.init();

当我们改造成上面的代码之后其实我们还能够优化。

bindEvents:()=>{
    hashTable={
        "#btnPauser":player.pause,
        "#btnPlay":player.play,
        "#btnSlow":player.slow,
        "#btnNormal":player.normal,
        "#btnFast":player.fast
    }
    for(let key in hashTable) {
        document.querySelector(key).onclick = hashTable[key]
    }
}

这样我们就实现了通过hash键值对的方式来控制按钮了。到了这里我们还可以简化,可以这个hashTable给提出去,形成单独的对象。这里要注意的是当我们events单独提出来时,会报错。因为这个时候player还没有声明我们就使用了。那么我们怎么解决呢?使用字符串的形式就可以了。那么问题又来了,在bindEvents中hashTable[key]的值就变了,怎么解决呢?使用一个变量接收events的key然后通过player[变量]就可以解决了

events:{
"#btnPauser":"pause",
"#btnPlay":"play",
"#btnSlow":"slow",
"#btnNormal":"normal",
"#btnFast":"fast"
}
bindEvents:()=>{
   for(let key in player.events) {
        const value = player.events[key];
        document.querySelector(key).onclick = player[value];
    } 
}

ok,到此位置基本上所有的代码都重构完成,我们把剩下的暴露在外的也甩进来即可。

import string from "./css.js";
const player = {
  n: 1,
  id: undefined,
  time: 100,
  ui: {
    demo: document.querySelector('#demo'),
    demo2: document.querySelector('#demo2')
  },
  init: () => {
    player.ui.demo.innerText = string.substr(0, player.n);
    player.ui.demo2.innerHTML = string.substr(0, player.n);
    player.bindEvents();
    player.play();
  },
  events: {
    '#btnPause': 'pause',
    '#btnPlay': 'play',
    '#btnSlow': 'slow',
    '#btnNormal': 'normal',
    '#btnFast': 'fast'
  },
  bindEvents: () => {
    for (let key in player.events) {
      if (player.events.hasOwnProperty(key)) {
        const value = player.events[key];
        document.querySelector(key).onclick = player[value];
      }
    }
  },
  run: () => {
    player.n += 1;
    if (player.n > string.length) {
      window.clearInterval(player.id);
      return;
    }
    player.ui.demo.innerText = string.substr(0, player.n);
    player.ui.demo2.innerHTML = string.substr(0, player.n);
    player.ui.demo.scrollTop = demo.scrollHeight;
  },
  play: () => {
    player.id = setInterval(player.run, player.time);
  },
  pause: () => {
    window.clearInterval(player.id);
  },
  slow: () => {
    player.pause();
    player.time = 300;
    player.play();
  },
  normal: () => {
    player.pause();
    player.time = 100;
    player.play();
  },
  fast: () => {
    player.pause();
    player.time = 0;
    player.play();
  }
}
player.init();

项目源码 项目链接