JavaScript 从入门到放弃

JavaScript 从入门到放弃

360前端星计划-第五课
主讲:月影老师

什么才是好的 JS 代码

各司其职

  • HTML=》结构
  • CSS=》样式
  • JavaScript=》行为

复杂 UI 组件的设计

了解其原理,能够更好的对其应用
例子–京东轮播图

  • 这样的 UI 组件如何去写
    步骤1:结构设计HTML
    • 图片结构是一个列表型结构,所以主体用
      • 使用 css 绝对定位将图片重叠在同一个位置
      • 轮播图切换的状态使用修饰符(modifier)
      • 轮播图的切换动画使用 css transition
    <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>
    
    • API 设计
      在这里插入图片描述
    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');
    setInterval(() => {
      slider.slideNext()
    }, 3000)
    
    • 控制流设计
      控制结构=》自定义事件
    class Slider{
      constructor(id, cycle = 3000){
        this.container = document.getElementById(id);
        this.items = this.container.querySelectorAll('.slider-list__item, .slider-list__item--selected');
        this.cycle = cycle;
    
        const controller = this.container.querySelector('.slide-list__control');
        if(controller){
          const buttons = controller.querySelectorAll('.slide-list__control-buttons, .slide-list__control-buttons--selected');
          controller.addEventListener('mouseover', evt=>{
            const idx = Array.from(buttons).indexOf(evt.target);
            if(idx >= 0){
              this.slideTo(idx);
              this.stop();
            }
          });
          
          controller.addEventListener('mouseout', evt=>{
            this.start();
          });
          
          this.container.addEventListener('slide', evt => {
            const idx = evt.detail.index
            const selected = controller.querySelector('.slide-list__control-buttons--selected');
            if(selected) selected.className = 'slide-list__control-buttons';
            buttons[idx].className = 'slide-list__control-buttons--selected';
          })
        }
        
        const previous = this.container.querySelector('.slide-list__previous');
        if(previous){
          previous.addEventListener('click', evt => {
            this.stop();
            this.slidePrevious();
            this.start();
            evt.preventDefault();
          });
        }
        
        const next = this.container.querySelector('.slide-list__next');
        if(next){
          next.addEventListener('click', evt => {
            this.stop();
            this.slideNext();
            this.start();
            evt.preventDefault();
          });
        }
      }
      getSelectedItem(){
        let selected = this.container.querySelector('.slider-list__item--selected');
        return selected
      }
      getSelectedItemIndex(){
        return Array.from(this.items).indexOf(this.getSelectedItem());
      }
      slideTo(idx){
        let selected = this.getSelectedItem();
        if(selected){ 
          selected.className = 'slider-list__item';
        }
        let item = this.items[idx];
        if(item){
          item.className = 'slider-list__item--selected';
        }
      
        const detail = {index: idx}
        const event = new CustomEvent('slide', {bubbles:true, detail})
        this.container.dispatchEvent(event)
      }
      slideNext(){
        let currentIdx = this.getSelectedItemIndex();
        let nextIdx = (currentIdx + 1) % this.items.length;
        this.slideTo(nextIdx);
      }
      slidePrevious(){
        let currentIdx = this.getSelectedItemIndex();
        let previousIdx = (this.items.length + currentIdx - 1) % this.items.length;
        this.slideTo(previousIdx);  
      }
      start(){
        this.stop();
        this._timer = setInterval(()=>this.slideNext(), this.cycle);
      }
      stop(){
        clearInterval(this._timer);
      }
    }
    
    const slider = new Slider('my-slider');
    slider.start();
    
    • 优化1:插件/依赖注入
    • 优化2:改进插件/模板化
    • 优化3:组件模型抽象

    局部细节控制

    在这里插入图片描述
    例子:消失的方块–处理“只能执行一次”

    • 有很多“只允许执行一次”的函数操作,如何进行统一的抽象?
      过程抽象
      once函数
    function once(fn){
      return function(...args){
        if(fn){
          let ret = fn.apply(this, args);
          fn = null;
          return ret;
        }
      }
    }
    

    应用–点击一次

    block.onclick = once(function(evt){
      console.log('hide');
      evt.target.className = 'hide';
      setTimeout(function(){
        document.body.removeChild(block);
      }, 2000);
    });
    

    节流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){
      let timer = null
      return function(...args){
        if(timer != null) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          fn.apply(this, args)
          timer = null
        }, 300)
      }
    }
    

    声明式(Declarative)编程和命令式(imperative)编程

    imperative:How to do?

    let list = [1, 2, 3, 4];
    let map1 = [];
    for(let i = 0; i < list.length; i++){
      map1.push(list[i] * 2);
    }
    

    Declarative:What to do?

    let list = [1, 2, 3, 4];
    const double = x => x * 2;
    list.map(double);
    

    高阶函数
    它们自身输入函数或返回函数,被称为高阶函数
    once、throttle、debounced、consumer、iterative等

    总结

    • 如何写好 JavaScript?
      各司其职:JavaScript 尽量只做状态管理
      结构、API、控制流分离设计 UI 组件
      插件和模板化,并抽象出组件模型
      运用过程抽象的技巧来抽象并优化局部 API
    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值