如何写好JavaScript | 青训营

84 阅读4分钟

如何写好JavaScript

写好JS的一些原则

  1. 让html、css、js各司其责
  2. 好的UI组件具备正确性、拓展性、复用性
  3. 应用函数式编程思想

深夜食堂代码优化

版本一有什么问题?

image.png 拿js做了css的事情

版本二

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

结论

  1. HTML、CSS、JS各司其责
  2. 应当避免不必要的由JS直接操作样式
  3. 可以用class来表示状态
  4. 纯展示类交互零js方案

组件封装

什么叫组件?

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

轮播图实现

  • 结构:HTML
  • 无序列表

image.png

  • 表现:CSS
  • 绝对定位、修饰符modifier、切换动画transition

image (1).png

  • 行为:JS
  • 行为设计:API(getSelectedItem、getSelectedItemIndex、slideNext、slidePrevious)
class Slider{
  constructor(id){
    this.container = document.getElementById(id);
    this.items = this.container
    .querySelectorAll('.slider-list__item, .slider-list__item--selected');
  }
  getSelectedItem(){
    const selected = this.container
      .querySelector('.slider-list__item--selected');
    return selected
  }
  getSelectedItemIndex(){
    return Array.from(this.items).indexOf(this.getSelectedItem());
  }
  slideTo(idx){
    const selected = this.getSelectedItem();
    if(selected){ 
      selected.className = 'slider-list__item';
    }
    const item = this.items[idx];
    if(item){
      item.className = 'slider-list__item--selected';
    }
  }
  slideNext(){
    const currentIdx = this.getSelectedItemIndex();
    const nextIdx = (currentIdx + 1) % this.items.length;
    this.slideTo(nextIdx);
  }
  slidePrevious(){
    const currentIdx = this.getSelectedItemIndex();
    const previousIdx = (this.items.length + currentIdx - 1)
      % this.items.length;
    this.slideTo(previousIdx);  
  }
}

const slider = new Slider('my-slider');
slider.slideTo(3);
  • 行为:控制流 使用自定义时间来解锁耦
const detail = {index: idx}
    const event = new CustomEvent('slide', {bubbles:true, detail})
    this.container.dispatchEvent(event)
<a class="slide-list__next"></a>
  <a class="slide-list__previous"></a>
  <div class="slide-list__control">
    <span class="slide-list__control-buttons--selected"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
    <span class="slide-list__control-buttons"></span>
  </div>

总结:基本方法

  1. 结构设计
  2. 展现效果
  3. 行为设计
  • API
  • Event

思考:改进空间

重构:插件化

解耦

  • 将控制元素抽取成插件
  • 插件与组件之间通过依赖注入方式建立联系

image.png

  • 将HTML模板化,更易于扩展

image (1).png image (2).png

抽象

将通用的组件模型抽象出来

image (3).png

image (4).png

总结

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

过程抽象

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

image (5).png

例子:操作次数限制

  • 一些异步交互
  • 一次性的HTTP请求

image.png

Once

为了能够让“只执行一次”的需求覆盖不同的时间处理,我们可以将这个需求剥离出来,这个过程我们称为过程抽象。

image (1).png

image (2).png

高阶函数

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

image (3).png

image (4).png

常用的高阶函数

  • Once
  • Throttle
  • Debounce
  • Consumer
  • Iterative

为什么要使用高阶函数

使用高阶函数(Higher-Order Functions)具有许多好处和优势,下面是其中的一些原因:

  1. 增强代码复用性:高阶函数可以接受其他函数作为参数或返回函数作为结果。这使得我们可以编写通用的函数,通过传递不同的函数作为参数来实现不同的功能。这可以大大增强代码的复用性,减少重复编写相似功能的代码。

  2. 提升代码的抽象程度:高阶函数能够提供更高层次的抽象,将常见的功能模式封装为可复用的函数。这可以使代码更加简洁和可读,将复杂的逻辑抽象为简单的函数调用。通过提升代码的抽象程度,可以更好地理解和维护代码。

  3. 实现函数的组合和管道:高阶函数使得函数的组合和管道操作变得更加容易。我们可以将多个函数组合在一起,以便按照特定的顺序应用它们,并将结果传递给下一个函数。这种函数组合和管道的方式可以使代码更加模块化和可组合。

  4. 支持函数式编程范式:高阶函数是函数式编程的重要概念之一。函数式编程强调将计算视为函数的应用,而不是一系列可变状态的操作。通过使用高阶函数,我们可以更加自然地编写函数式风格的代码,避免副作用和共享状态,使代码更加可靠和易于测试。

  5. 扩展函数的功能:高阶函数允许我们在不改变原始函数定义的情况下,通过包装函数来扩展其功能。这种思想通常被称为函数装饰器(Function Decorators),可以用于添加日志记录、缓存、错误处理等功能,从而增强原始函数的能力。

总而言之,使用高阶函数可以提升代码的复用性、抽象程度和可组合性,支持函数式编程范式,并扩展函数的功能。通过灵活运用高阶函数,我们可以编写更加模块化、可读性强且容易维护的代码。

编程范式

命令式与声明式

  • 命令式编程: 命令式编程是一种通过编写明确的指令和算法来实现程序的方式。
  • 声明式编程: 声明式编程是一种通过描述所需的结果,而非具体的步骤和算法来实现程序的方式。

image (5).png

例子

  • Toggle-命令式
switcher.onclick = function(evt){
  if(evt.target.className === 'on'){
    evt.target.className = 'off';
  }else{
    evt.target.className = 'on';
  }
}
  • Toggle-声明式
function toggle(...actions){
  return function(...args){
    let action = actions.shift();
    actions.push(action);
    return action.apply(this, args);
  }
}

switcher.onclick = toggle(
  evt => evt.target.className = 'off',
  evt => evt.target.className = 'on'
);
  • Toggle-三态
function toggle(...actions){
  return function(...args){
    let action = actions.shift();
    actions.push(action);
    return action.apply(this, args);
  }
}

switcher.onclick = toggle(
  evt => evt.target.className = 'warn',
  evt => evt.target.className = 'off',
  evt => evt.target.className = 'on'
);