JavaScript(实例) | 青训营笔记

55 阅读2分钟

这是我参与 第五届青训营 伴学笔记创作活动的第7天

写代码最应该关注什么?

  • 为自己,清晰、职责分明、可维护的代码有利于后期迭代
  • 为他人,离职了以后同事能更好地交接
  • 观察并吸收同事优秀的代码风格
  • 代码可读性放在第一位
  • 注意大的性能优化
  • 学习并使用设计模式(理解业务的基础上)

交通灯

交通灯切换状态代码

是实现一个切换多个交通灯状态切换的功能。

版本一

setTimeOut多层嵌套

使用setTimeOut函数来实现这一功能。但由于它是异步的,需要嵌套五层。虽然代码是可以运行的,但它的可维护性和美观程度都比较差。

const traffic = document.getElementById('traffic');

(function reset(){
  traffic.className = 's1';
  
  setTimeout(function(){
      traffic.className = 's2';
      setTimeout(function(){
        traffic.className = 's3';
        setTimeout(function(){
          traffic.className = 's4';
          setTimeout(function(){
            traffic.className = 's5';
            setTimeout(reset, 1000)
          }, 1000)
        }, 1000)
      }, 1000)
  }, 1000);
})();

版本二

数据抽象

定义一个start方法,使用递归的方式更加优雅地实现了这一功能,交通灯的不同状态抽象成了列表stateList方便状态的增删与修改。

const traffic = document.getElementById('traffic');

const stateList = [
  {state: 'wait', last: 1000},
  {state: 'stop', last: 3000},
  {state: 'pass', last: 3000},
];

function start(traffic, stateList){
  function applyState(stateIdx) {
    const {state, last} = stateList[stateIdx];
    traffic.className = state;
    setTimeout(() => {
      applyState((stateIdx + 1) % stateList.length);
    }, last)
  }
  applyState(0);
}

start(traffic, stateList);

版本三

过程抽象

将轮巡poll与等待wait的过程定义成了单独的方法,使用到了过程抽象的概念。虽然过程抽象后得到的代码明显变多,但得到了通用的过程方法,可以更方便地扩展到其他领域,更加灵活。

const traffic = document.getElementById('traffic');

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function poll(...fnList){
  let stateIndex = 0;
  
  return async function(...args){
    let fn = fnList[stateIndex++ % fnList.length];
    return await fn.apply(this, args);
  }
}

async function setState(state, ms){
  traffic.className = state;
  await wait(ms);
}

let trafficStatePoll = poll(setState.bind(null, 'wait', 1000),
                            setState.bind(null, 'stop', 3000),
                            setState.bind(null, 'pass', 3000));

(async function() {
  // noprotect
  while(1) {
    await trafficStatePoll();
  }
}());

版本四

异步、函数式

版本三中的代码相对来说不容易理解。而这一版本地代码就提高了代码的可读性,更贴近自然的理解模式。对于轮巡操作,主要包括状态的设置和等待若干时间后再设置下一个状态,前者可以通过setState实现,后者可以通过wait方法实现。

const traffic = document.getElementById('traffic');

function wait(time){
  return new Promise(resolve => setTimeout(resolve, time));
}

function setState(state){
  traffic.className = state;
}

async function start(){
  //noprotect
  while(1){
    setState('wait');
    await wait(1000);
    setState('stop');
    await wait(3000);
    setState('pass');
    await wait(3000);
  }
}

start();

2023/1/24 Day7