JavaScript代码质量的优化 | 青训营笔记

68 阅读4分钟

阿尔卑斯山风景4k高清壁纸3840x2160_彼岸图网.jpg 这是我参与「第五届青训营 」伴学笔记创作活动的第 3 天。

重点内容:🍊JavaScript好的代码的标准🍋编程范式🍑优化之路

🥑前言🥑

本节课程老师带我们深入JavaScript,了解了什么是好的代码,什么是规范,以及如何做的更优雅!

🍆JavaScript好的代码的标准🍆

各司其职:HTML/CSS/JS 职能分离。HTML负责结构,CSS负责表现,JS负责行为。

  • 应当避免不必要的由JS直接操作样式
  • 可以用class来表示状态
  • 纯展示类交互寻求零JS方案

78bf61dc10e342d2ab3a12f1a7b23171_tplv-k3u1fbpfcp-watermark.png

组件封装:好的UI组件具备正确性、扩展性、复用性。

组件是指web页面上抽出来一个个包含模板、功能和样式的单元。
好的组件具备封装性、正确性、扩展性、复用性。

过程抽象:应用函数式编程思想。

  • 用来处理局部细节控制的一些方法
  • 函数式编程思想的基础应用

🥔编程范式🥔

  • 命令式更趋向于怎么做
  • 声明式更趋向于做什么
  • 声明式的编程思想天然的就比命令式有更强的可扩展性!

🥕JavaScript代码质量的优化🥕

前文中所阐述的种种,都是告诉我们什么是好的,好的规范是什么。接下来,我们就跟随月影老师的交通灯案例,一起来深入了解一下如何提高代码质量,做到简洁而优雅吧~

🌰:

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);
})();

这是案例最简单无脑的方法,用setTimeout实现,但因为setTimeout是异步的,所以需要层层嵌套来实现五个切换。当然,代码本身是可以运行的,但是在项目开发或者说团队中,这样的代码太过丑陋而且由于层层嵌套,所以很难去修改,难以维护。

那么该如何进行优化呢?让我们看一下下面的代码。⬇️

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);

这段代码最大的优点就是对上述代码进行数据抽象把状态抽象出来放在一个状态列表中。然后定义一个start方法来使用状态列表切换元素。这样做的好处是如果想要添加新的状态,只需要在状态列表中添加即可,而不用去复杂的setTimeout嵌套中去想应该放在哪里,在什么位置。

那么有没有更通用的模板呢?⬇️

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();
  }
}());

乍一看,这段代码又长又复杂,事实上,他确实如此。😂但是它实现了可扩展性呀!
这一段代码是通过过程抽象来实现红绿灯的转换的。先定义一个wait方法表示等待多长时间,poll表示轮询,即后续绑定后,直接可以进行循环。setState设置元素状态和持续时间。调用trafficStatePoll,将绑定相关的状态和等待参数,直接进行循环。虽然写的代码很多,但是可以扩展到其他领域的切换。有灵活性和可扩展性!在其他需要循环显示的场景里,可以直接在poll中添加绑定信息。

可是它还是好复杂,有没有简单易懂更符合我们标准的方法呢?⬇️

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();

这段代码是采用异步+函数式的编程实现的。主要思路是定义一个等待若干时间函数wait,再定义一个设置状态函数,最后用一个异步函数start来启动,每调用一次setState,就等待,如此循环即可实现功能。怎么样?惊不惊喜,意不意外!😁

🍄总结🍄

代码的世界,就是这样丰富。我们了解了什么是好的之后,一定不能局限于解决某一问题即可,也要想一想有没有更简单的方法?有没有更好的写法?能不能少一点?如你所见,同样的问题,当我们不断优化代码之后,就是可以做到简洁而优雅~