如何写好JavaScript | 青训营笔记

87 阅读3分钟

这是我参与「第四届青训营 」笔记创作活动的第1天

如何写好JavaScript?

推荐书籍

  • 犀牛书 《JavaScript权威指南》
  • 红宝书 《JavaScript高级程序设计》

写好JS的一些原则

各司其责

让HTML、CSS、JavaScript职能分离。

例子

写一段JS,控制一个网页,切换浅色深色主题

思路一

利用document API获取按钮 绑定事件 切换body的style样式 同时利用事件对象改变按钮Html内容 [JS代码多且不遵循各司其责]

思路二

与思路一的差别在于 先设置好对应主题样式的class 点击改变className即可 [更加简洁且遵循各司其责]

思路三

零JS实现 利用伪类选择器

核心代码

<input id="check" type="checkbox">
<label id="btn" for="check"> </label><style>
    #check{
        display:none; //隐藏
    }
    //伪类选择器 
    #check:checked+.content{
        background-color:black;
        color:white;
        transition:all 1s;
    }
</style>

总结

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

组件封装

好的UI组件具备正确性、拓展性、复用性

例子

用原生JS写一个电商网站的轮播图

结构

典型的列表结构,可以使用无序列表ul元素来实现。

表现
  • 使用CSS绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符 modifier
  • 轮播图的切换动画使用CSS transition
行为
  • Slider

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

    核心代码

            class Swiper{
                constructor(id){
                    this.container = document.getElementById(id);
                    this.items = this.container.querySelectorAll('.item-selected,.item')
                    console.log(this.items);
                }
                getSelectedItem(){
                    const selected = this.container.querySelector('.item-selected');
                    console.log(selected);
                    return selected
                    //获取选中元素
                }
                getSelectedItemIndex(){
                    return Array.from(this.items).indexOf(this.getSelectedItem)
                    //获取选中元素的下标   from方法转化为数组
                }
                slideTo(idx) {
                    //切换至对应下标
                    const selected = this.getSelectedItem()
                    if(selected){
                        selected.className = 'item' //把原来的选中类名去除
                    }
                    const item = this.items[idx]
                    console.log(item);
                    if(item){
                        item.className = 'item-selected'
                    }
                }
                slideNext(){
                    //切换至下一个
                    const cur = this.getSelectedItemIndex();
                    const next =( cur + 1 ) %this.items.length;
                    this.slideTo(next)
                }
                slidePrevious(){
                    //切换至前一个
                    const cur = this.getSelectedItemIndex();
                    const pre = (this.items.length+cur-1)%this.items.length;
                    this.slideTo(pre)
                }
               
    ​
            }
            const swiper = new Swiper('swiper')
            swiper.slideTo(2)
    
  • 结构设计

  • 展现效果

  • 行为设计

    • API(功能)
    • Event(控制流)

思考

重构组件

解耦

  • 将控制元素抽取成插件
  • 插件与组件之间通过依赖注入方式建立联系
  • 将HTML模板化,更易于扩展,能通过传入数据灵活改变结构

总结

  • 组件设计的原则:封装性、正确性、扩展性、复用性

  • 实现组件的步骤:结构设计、展现效果、行为设计

  • 三次重构

    • 插件化
    • 模板化
    • 抽象化(组件框架)

过程抽象

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

例子

操作次数限制

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

核心代码

 const list = document.querySelector('ul');
  const buttons = list.querySelectorAll('button');
  buttons.forEach((button) => {
    button.addEventListener('click', (evt) => {
      const target = evt.target;
      target.parentNode.className = 'completed';
      setTimeout(() => {
        list.removeChild(target.parentNode);
      }, 2000);
    });
  });

连续快速多次点击报错

解决方法

利用高阶函数

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

 function once(fn) {
    return function(...args) {
      if(fn) {
        const ret = fn.apply(this, args);
        fn = null;
        return ret;
      }
    }
  }

HOF

  • 以函数作为参数
  • 以函数作为返回值
  • 常用于作为函数装饰器
function HOF0(fn) {
    return function(...args) {
      return fn.apply(this, args);
    }
  }
function HOF0(fn) {
    return function(...args) {
      return fn.apply(this, args);
    }
  }
​

常用高阶函数

编程范式

  • 声明式
let list = [1, 2, 3, 4];
  const double = x => x * 2;
  list.map(double);
  • 命令式
let list = [1, 2, 3, 4];
  let mapl = [];
  for(let i = 0; i < list.length; i++) {
    mapl.push(list[i] * 2);
  }

例子

\