【青训营】- 写好JS

各司其职

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

例如:实现一个网页的“夜间模式”

image.png
方法一:

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 = '';
  }
});
复制代码

缺点

  • 程序用了js直接操作样式
  • 代码显得复杂繁琐、且复用性差

改进:

  • 可以用class来表示状态

方法二:

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

优点:

  • 相比方法一没有用js直接操作样式

改进:

  • 可以用纯css实现此效果,让代码更简洁

方法三:(推荐)

<input id="modeCheckBox" type="checkbox">
  <div class="content">
    <header>
      <label id="modeBtn" for="modeCheckBox"></label>
      <h1>深夜食堂</h1>
    </header>
    <main>
        ······
    </main>
   </div> 
复制代码
#modeCheckBox:checked + .content {
  background-color: black;
  color: white;
  transition: all 1s;
}
复制代码

优点:

  • 是非常好的零js展示方案
  • 完全满足了HTML/CSS/JS各司其职的书写理念

组件封装

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

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

实现组件的步骤:

如轮播图为例:轮播图

结构设计:HTML

轮播图是一个典型的列表结构,可以使用无序列表

    元素来实现

    <div id="my-slider" class="slider-list">
      <ul>
        <li class="slider-list__item--selected">
          <img src="https://p5.ssl.qhimg.com/t0119c74624763dd070.png"/>
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01adbe3351db853eb3.jpg"/>
        </li>
        <li class="slider-list__item">
          <img src="https://p2.ssl.qhimg.com/t01645cd5ba0c3b60cb.jpg"/>
        </li>
        <li class="slider-list__item">
          <img src="https://p4.ssl.qhimg.com/t01331ac159b58f5478.jpg"/>
        </li>
      </ul>
    </div>
    复制代码

    展现效果:CSS

    • 使用CSS绝对定位将图片重叠在同一个位置
    • 轮播图切换的状态使用修饰符(modifier)
    • 轮播图切换动画使用CSS transition
    #my-slider{
      position: relative;
      width: 790px;
    }
    
    .slider-list ul{
      list-style-type:none;
      position: relative;
      padding: 0;
      margin: 0;
    }
    
    .slider-list__item,
    .slider-list__item--selected{
      position: absolute;
      transition: opacity 1s;
      opacity: 0;
      text-align: center;
    }
    
    .slider-list__item--selected{
      transition: opacity 1s;
      opacity: 1;
    }
    复制代码

    行为设计

    API(功能)

    API设计应保证原子操作,职责单一,满足灵活性。

    • Slider

      • +getSelectedItem( ) 当前选中被展现出的图
      • +getSelectedItemIndex( ) 当前选中被展现出的图的索引
      • +slideTo( ) 跳转并展示某个图
      • +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(1);
    复制代码

    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>
    复制代码

    使用自定义事件来解耦

      const detail = {index: idx}
      const event = new CustomEvent('slide', {bubbles:true, detail})
      this.container.dispatchEvent(event)
    复制代码

    组件封装优点:

    • 复用性强
    • 可读性好
    • 加强自己的代码管理能力

    过程抽象

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

    函数式编程是一种编程范式,我们常见的编程范式有命令式编程(Imperative programming)函数式编程逻辑式编程,常见的面向对象编程是也是一种命令式编程。

    命令式编程是面向计算机硬件的抽象,有变量(对应着存储单元),赋值语句(获取,存储指令),表达式(内存引用和算术运算)和控制语句(跳转指令),一句话,命令式程序就是一个冯诺依曼机指令序列

    而函数式编程是面向数学的抽象,将计算描述为一种表达式求值,一句话,函数式程序就是一个表达式

    函数式编程的本质

    函数式编程中的函数这个术语不是指计算机中的函数(实际上是Subroutine),而是指数学中的函数,即自变量的映射。也就是说一个函数的值仅决定于函数参数的值,不依赖其他状态。

    比如sqrt(x)函数计算x的平方根,只要x不变,不论什么时候调用,调用几次,值都是不变的。相同的输入总是得到相同的输出,复用性好

    1、表达式化

    函数式编程的思维就是首先转变我们传统的编程观念。去可变量,去循环,把命令式改成声明式

    命令式:
    carbon (2).png

    声明式

    carbon (3).png

    2、高阶逻辑

    用了函数式,思维就要从循环、赋值这些低阶逻辑转移成高阶的思考问题。函数式又叫声明式,也就是需要做什么操作,只需要说一下就行,而非写个遍历,做个状态判断.

    用函数式就不需要考虑这样,你不知道函数式的列表是怎么遍历的,中间向两边? 从后往前?这也是为何函数式适合并发的原因之一,你想知道列表中大于3的数有多少,只要,list.count(_ > 3) 而不是写循环,你可以直接写你的业务,不要拘泥于细节,有点像sql, 你需要什么告诉电脑就行,你或许会问,count foreach filter 这些函数怎么来的? 因为有了他们你才不需要写循环,他们把你留在高阶逻辑中。

    3、组合子逻辑 或又叫 自底向上的设计

    面向对象是自顶向下的设计,函数式是自底向上的设计,也就是先定义最基本的操作,然后不断组合,不断堆积以满足你的所有需要,如sql定义了select, from, where…这几个组合子,来满足你的查询需求,同理函数式语言会提供foreach, map等组合子(操作)来满足你的需求,所以你必须自下而上的设计你的代码结构,并且满足你的需求,当你只用组合子写代码时,你会发现你写的全是高阶逻辑

    总结:

    函数式思维,其实就是组合子逻辑,用简单的几个函数组合来构建复杂逻辑,始终以高阶的角度去表达问题,而非依赖副作用。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享