如何写好JavaScript | 青训营笔记

47 阅读2分钟

这是我参与「第四届青训营 」笔记创作活动的第二天
今天跟着老师学习了一些使JS代码更优秀的原则。

本篇笔记的基本内容

  • 写好JS的一些原则,包括职能分离、组件封装、过程抽象等等。

写好JS的原则

让HTML、CSS和JavaScript职能分离

简单来说,就是让HTML、CSS和JavaScript分别干它们擅长的事,比如,最好不要直接用JS操纵网页内容,最好不要在HTML内部写样式等等。
优化前:

  const btn = document.getElementById('modeBtn');
  btn.addEventListener('click', (e) => {
    const body = document.body;
    if(e.target.innerHTML === '🌞') {
      body.style.backgroundColor = 'black';
      body.style.color = 'white';
      e.target.innerHTML = '🌜';
    } else {
      body.style.backgroundColor = 'white';
      body.style.color = 'black';
      e.target.innerHTML = '🌞';
    }
  });

优化后:

const btn = document.getElementById('modeBtn');
  btn.addEventListener('click', (e) => {
    const body = document.body;
    if(body.className !== 'night') {
      body.className = 'night';
    } else {
      body.className = '';
    }
  });

优化后的代码避免了对样式的直接操作。
结论:

  • HTML/CSS/JS 各司其职
    HTML用于构建框架,CSS用于增加样式,JS用于编写行为。
  • 尽量避免不必要的由JS直接操作样式
  • 可以用 class 来表示元素的状态
  • 纯展示类交互寻求零 JS 方案,比如使用 :hover 选择器等。

组件封装

组件封装可以将能够多次使用的代码提取出来,避免了代码的重复书写,减少代码冗余,增加了代码的复用性。
结论:

  • 组件设计的原则:封装性、正确性、扩展性、复用性
  • 实现组件的步骤:
    1. 结构设计
    2. 展现效果、
    3. 行为设计(API (功能)、Event (控制流))
  • 三次重构
    1. 插件化
    2. 模板化
    3. 抽象化(组件框架)

过程抽象

假设有一个方法可以处理一个事件,我们将这个处理过程提取出来,减少它的针对性,使其能够处理所有类似的事件,这样的出库叫做过程抽象
这样抽象出来的函数叫做高阶函数(HOF),它的特征有:

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器


    常用的高阶函数:
  • Once
function once(fn) {
  return function(...args) {
    if(fn) {
      const ret = fn.apply(this, args);
      fn = null;
      return ret;
    }
  }
}
  • Throttle
function throttle(fn, time = 500){
  let timer;
  return function(...args){
    if(timer == null){
      fn.apply(this,  args);
      timer = setTimeout(() => {
        timer = null;
      }, time)
    }
  }
}
  • Debounce
function debounce(fn, dur){
  dur = dur || 100;
  var timer;
  return function(){
    clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, arguments);
    }, dur);
  }
}
  • Consumer
function consumer(fn, time){
  let tasks = [],
      timer;
  
  return function(...args){
    tasks.push(fn.bind(this, ...args));
    if(timer == null){
      timer = setInterval(() => {
        tasks.shift().call(this)
        if(tasks.length <= 0){
          clearInterval(timer);
          timer = null;
        }
      }, time)
    }
  }
}
  • Iterative
function iterative(fn) {
  return function(subject, ...rest) {
    if(isIterable(subject)) {
      const ret = [];
      for(let obj of subject) {
        ret.push(fn.apply(this, [obj, ...rest]));
      }
      return ret;
    }
    return fn.apply(this, [subject, ...rest]);
  }
}

总结

书写JS代码时,应注意好JS的职能,对常用的部分进行组件封装,对常用的逻辑进行过程抽象。