如何写好JavaScript | 青训营笔记

72 阅读4分钟

JavaScript 编码原则

写好 JS 的一些原则:

  1. 各司其责

    让 HTML、CSS 和 JavaScript 职能分离。

  2. 组件封装

    好的 UI 组件具有正确性、扩展性、复用性。

  3. 过程抽象

    应用函数式编程思想。

各司其责

如果需要通过 JS 来实现样式的修改,不要在 JS 中通过 style 修改样式,而是将样式定义在 CSS 中,通过修改类名来修改样式。

例如:

 // 通过 JS 修改样式
 const body = document.body;
 if(e.target.innerHTML === 'white') {
   body.style.backgroundColor = 'black';
   body.style.color = 'white';
   e.target.innerHTML = 'black';
 } else {
   body.style.backgroundColor = 'white';
   body.style.color = 'black';
   e.target.innerHTML = 'white';
 }
 ​
 // 通过 JS 修改类名
 const body = document.body;
 if(body.className === 'white') {
   body.className = 'black';
 } else {
   body.className = 'white';
 }

更优解——纯 CSS 实现(如果只修改样式,而没有其他逻辑)

 <!-- 将原来的 button 改为以下格式 -->
 ​
 <input id="modeCheckBox" type="checkbox">
 <!-- 将原来放 button 的位置替换为 label -->
 <!-- 通过 label 的 for 属性将 label 指定为 id 为 modeCheckBox 的 input,即:点击 label 的效果和点击 input 的效果一致 -->
 <label id="modeBtn" for="modeCheckBox">
 /*
   css 中的样式
 */
 ​
 #modeCheckBox {
   /* label 实现了原来 button 的效果,将 input 隐藏起来更美观 */
   display: none;
 }
 ​
 /* 当 input 为选中时,其类名为 content 的兄弟标签改变样式,自身的样式也会更改,但是不需要关注 */
 #modeCheckBox:checked + .content {
   background-color: black;
   color: white;
 }
 ​
 /* 不需要初始化 .content 的样式,因为默认为白底黑字 */

总结

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

组件封装

组件是指 Web 页面上抽出来一个个包含模板(HTML)、样式(CSS)和功能(JS)的单元。

好的组件具备封装性正确性扩展性复用性

例如:一个轮播图

  • 结构(HTML)

    轮播图是一个典型的列表结构,可以使用无序列表 ul 元素来实现。

  • 表现(CSS)

    • 使用 CSS 绝u第定位将图片重叠在同一个位置
    • 轮播图切换的状态使用修饰符(modifier)
    • 轮播图的切换动画使用 CSS transition
  • 行为(JS)

    Slider

    • getSelectedItem()
    • getSelectedItemIndex()
    • slideTo()
    • slideNext()
    • slidePrevious()

    控制流

    • 使用自定义事件来解耦

重构

插件化

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

模板化

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

抽象化

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

总结

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

过程抽象

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

高阶函数

概念

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

常用高阶函数

  • Once
  • Throttle
  • Debounce
  • Consumer
  • Itrative

once

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

 // DOM 事件只触发一次的方式
 ​
 // 方式一:设置 options 配置项
 btn.addEventListener('click', ()=>{}, {
   once: true
 })
 ​
 // 方式二:将回调函数配置为高阶函数
 function once(fn) {
   return function(...args) {
     if(fn) {
       const res = fn.apply(this, args);
       fn = null;
       return res;
     }
   }
 }
 btn.addEventListener('click', once(()=>{}))

consumer

每隔一段时间调用一次指定的函数。

 function consumer(fn, time) {
   let tasks = [], timer;
   return function(...args) {
     tasks.push(fn.bind(this, ...args));
     if(!timer) {
       timer = setInterval(() => {
         tasks.shift[].call(this);
         if(tasks.length <= 0) {
           clearInterval(timer);
           timer = null;
         }
       }, time);
     }
   }
 }

iterative

批量操作函数。

 function iterative(fn) {
   const isIterable = obj => {
     return obj && typeof obj[Symbol.iterator] === 'function';
   }
   return function(subject, ...rest) {
     if(isIterable(subject)) {
       const res = [];
       for(let obj of subject) {
         res.push(fn.apply(this, [obj, ...rest]));
       }
       return res;
     }
     return fn.apply(this, [subject, ...rest]);
   }
 }

为什么要使用高阶函数?

  • 纯函数:在任何时候都可以预期结果的函数。

    容易测试。

  • 非纯函数:不能预期结果的函数。

    难以测试,项目越多越难维护。

高阶函数可以降低非纯函数的使用度,利于项目维护。

编程范式

命令式与声明式

  • 命令式:强调怎么做

    简单,不利于维护。

     // 例子:点击按钮切换状态
     ​
     btn.onclick = function(el) {
      if(el.target.className === 'on') {
        el.target.className = 'off';
      } else {
        el.target.className = 'on';
      }
     }
    
  • 声明式:强调做什么

    复杂,利于维护。

     // 例子:点击按钮切换状态
     ​
     function toggle(...actions) {
      return function(...args) {
        let action = actions.shift();
        actions.push(aciton);
        return action.apply(this, args);
      }
     }
     btn.onclick = toggle(
       el => el.target.className = 'off',
      el => el.target.className = 'on'
     )
    

代码质量优化

  • 避免回调地狱,采用异步实现更加清晰
  • 在特点情况下,用生成器函数替换普通函数

总结

  1. 能用 HTML、CSS 实现的功能不要用 JS
  2. 代码尽量不要为了简单而写,要尽量为了复用性、扩展性而写
  3. 一个项目如果可以的化尽量拆分成多个小项目
  4. 代码要注重时、空复杂度