如何写好Javascript

247 阅读3分钟

360前端星计划学习笔记day2

1. 各司其职

css和js功能分离

2. 复杂UI组件的设计

例子:实现一个轮播图

步骤一:结构设计

基本结构(使用bem命名规范)

<div id="my-slider" class="slider-list">
    <ul>
        <li class="slider-list__item--selected">
            <img src=""/>
        </li>
         <li class="slider-list__item">
            <img src=""/>
        </li>
         <li class="slider-list__item">
            <img src=""/>
        </li>
         <li class="slider-list__item">
            <img src=""/>
        </li>
    </ul>
<div>
  • 图片结构是列表型结构,使用<ul>
  • 使用css绝对定位将图片重叠在同一个位置
  • 轮播图切换的状态使用修饰符(modifier)
  • 轮播图的切换动画用css transition

控制结构: 左右和下面小圆点按钮

<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>

步骤二 API设计

  • getSelectedItem()
  • getSelectedItemIndex() 鼠标到下面小圆点
  • slideTo()
  • slideNext() 上一个
  • slidePrevious() 下一个
  • setInterval() 做切换
  • 自定义事件:激活小圆点的状态,图片切换时候小圆点对应切换
  • stop() 停止定时器
  • start() 打开定时器

改进

  • 小圆点和左右按钮使用插件,plugin(this)依赖注入,降低耦合度
  • 模版化:把html代码整合在js中,只需要维护一处地方,在render()中设置html的内容
    组件是数据和行为驱动的,结构交给组件内部机制生成
  • 组件模型抽象:通用抽象类Component可以用来扩展其他组件,在这个组件中注册插件,渲染data,Slider组件继承Component,从而形成一个UI组件库雏形

3. 局部细节控制

只允许执行一次

  • addEventListener设置once
  • onclick调用一次以后设置block.onclick = null

很多操作都需要只执行一次-->统一抽象 -->高阶函数

自身输入是一个函数,或者返回值是函数,被称为高阶函数

过程抽象:

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

节流:用于scroll或者mousemove这种执行次数比较多的执行函数

   function throttle(fn,time=500){
       let timer;
       return function(...args){
           if(timer == null){
               fn.apply(this,args);
               timer = setTimeout(()=>{
                   timer = null;
               },time)
           }
       }
   }

防抖:只需要得到用户最终状态的情况

   function debounce(fn,dur){
       dur = dur || 100;
       var timer;
       return function(){
           clearTimeout(timer);
           timer = setTimeout(()=>{
               fn.apply(this,arguments);
           },dur);
       }
   }

消费者:每次执行的时候push到任务队列里,每隔多长时间执行一次,把同步执行操作变为异步执行操作。使用consumer可以实现连击事件。

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

声明式编程和指令式编程

  • 声明:关注做什么
  • 指令:关注怎么做

iterative

多个reduce实现通用性

    function iterative(fn){
        return function(...args){
            return args.reduce(fn.bind(this));
        }
    }
    const add = iterative((x,y)=>x+y);
    const sub = iterative((x,y)=>x-y);

声明式的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',
        evt => evt.target.className = 'warn',
    )

使用生成器

    function * loop(list,max=Infinity){
        let i = 0;
        while(i<max){
            yield list[i++ % list.length];
        }
    }
    function toggle(...actions){
    let action = loop(actions);
        return function(...args){
            return action.next().value.apply(this,args);
        }
    }

总结

  • 各司其职:js做状态管理
  • 结构、API、控制流分离设计UI组件
  • 插件和模版化,并抽象出组件模型
  • 运用过程抽象的技巧来抽象并优化局部API