写好JavaScript的三个准则:各司其责 组件封装 过程形象

各司其责

  • HTML/CSS/JavaScript各司其责

    • HTML -> Structural ; CSS -> Presentational ; JavaScript -> Behavioral
  • 该当防止不必要的由JS间接操作款式
  • 能够用class来示意状态
  • 纯展现类交互应寻求零JS计划

组件封装

组件是指Web页面上抽出来的一个个蕴含模块(HTML)、款式(CSS)和性能(JS)的单元。好的组件具备封装性、正确性、扩展性、复用性。实现组件的步骤:结构设计、展示成果、行为设计,三次重构:插件化重构、模板化重构、抽象化重构。

  • 结构设计:HTML
  • 展示成果:CSS
  • 行为设计:JS

    • API(性能),API 设计应保障原子操作,职责繁多,满足灵活性。
    • Event(控制流),应用自定义事件来解耦。
  • 插件化重构,即解耦

    • 将管制元素抽取成插件
    • 插件与组件之间通过依赖注入形式建立联系
  • 模板化重构

    • 将HTML模板化,更易于扩大
  • 抽象化重构(组件框架)

    • 将通用的组件模型形象进去

过程形象

过程形象是指用来解决部分细节管制的一些办法,是函数式编程思维的根底利用。

  • 高阶函数

    • 以函数作为参数
    • 以函数作为返回值
    • 罕用于函数装璜器
//零阶高阶函数,等价于间接调用函数function HOF_0 (fn){    return function(...args){        return fn.apply(this.args);    }}
  • 结构 once 高阶函数,为了可能让“只执行一次”的需要(例如一些异步操作、一次性的HTTP申请)笼罩不同的事件处理,咱们能够将这个需要利用闭包剥离进去。这个过程称为过程形象。
function once ( fn ) {    return function (...args){        const res = fn.allpy (this.args);        fn = null;        return res;    }}
  • 防抖函数,在第一次触发事件时,不立刻执行函数,而是给出一个期限值,如果在期限值内没有再次触发滚动事件,那么就执行函数,如果在期限值内再次触发滚动事件,那么以后的计时勾销,从新开始计时

    const debounce = (fn, delay) => {  let timer = null;    //借助闭包  return function () {      clearTimeout(timer);      timer = setTimeout(fn, delay);  }}
  • 节流函数,相似管制阀门一样定期凋谢的函数,也就是让函数执行一次后,在某个时间段内临时生效,过了这段时间后再从新激活。成果:如果短时间内大量触发同一事件,那么在函数执行一次之后,该函数在指定的工夫期限内不再工作,直至过了这段时间才从新失效。

    const myThrottle = (fn, delay) => {  let flag = true;  return function () {      if (!flag) {          return false;      } else {          flag = false;          setTimeout(() => {              fn();              flag = true;          }, delay);      }  }}
  • 为什么要应用高阶函数?

    • 缩小零碎内非纯函数的数量,进步零碎的可测试性和稳定性。

写好 JavaScript

写代码应该关注

  • 效率
  • 格调
  • 应用场景
  • 约定
  • 设计
    具体的代码实现要因场景而定,不同的场景重视点不一样,例如在某些比拟底层的场景,可能更重视效率,而在多人合作的时候可能更关注约定。

判断是否是4的幂

  • 惯例操作

    const isPowerOfFour = (num) => {   num = parseInt(num);   while(num > 1){       if(num % 4) return false;       num /= 4;   }   return true;}
  • 优化版本1,利用 4 的幂的二进制数最高位为1,低位为偶数个0

    const isPowerOfFour = (num) => {  num = parseInt(num);  return num > 0 &&         (num & (num-1)) === 0 &&         (num & 0XAAAAAAAA) === 0;}
  • 优化版本2,利用正则表达式
const isPowerOfFour = (num) => {    num = parseInt(num).toString(2);    return /^1(?:00)*$/.test(num);}

实现交通灯切换成果

  • 版本一,利用setTimeout,可能呈现回调天堂

    const traffic = document.getElementById('traffic'); (function reset(){   traffic.className = 's1';   setTimeout(function(){       traffic.className = 's2';       setTimeout(function(){           traffic.className = 's3';           setTimeout(function(){               traffic.className = 's4';               setTimeout(function (){                   traffic.className = 's5';                   setTimeout(reset,1000)               },1000)           },1000)       },1000)   },1000) })();
  • 优化版本,利用async/await

    const traffic = document.getElementById('traffic'); function wait(time){   return new Promise(resolve => setTimeout(resolve,time)) } function setState(state){   traffic.className = state;} async function start(){   while(1){       setState('wait');       await wait(1000);       setState('stop');       await wait(3000);       setState('pass');       await wait(3000);  } } start();

洗牌算法

  • 谬误示例
    看似能够正确洗牌,但实际上较小的牌放到后面的概率更大,较大的的牌放到前面的概率更大

    const cards = [0,1,2,3,4,5,6,7,8,9];const shuffle = (cards) => {  return [...cards].sort(() => { Math.random() > 0.5 ? -1 : 1 });}
  • 正确示例
const cards = [0,1,2,3,4,5,6,7,8,9]; function *draw(cards){     const c = [...cards];     for(let i = c.length ; i > 0 ; i--){         const pIdx = Math.floor(Math.random() * i);        [c[pIdx],c[i - 1]] = [c[i - 1],c[pIdx]];         yield c[i - 1];    }     return c; }

分红包问题

  • 版本一,相似于切蛋糕,取最大的持续切分,得出的后果会比拟均匀

    function generate(amount,count){   let ret = [amount];   while(count > 1){       // 挑出最大的进行切分       let cake = Math.max(...ret),       idx = ret.indexOf(cake),             part = 1 + Math.floor((cake / 2) * Math.random()),             rest = cake - part;       ret.splice(idx,1,part,rest);       count --;   }   return ret; }
  • 版本二,随机性较大,可能会有较大的红包呈现
function *draw(cards){     const c = [...cards];     for(let i = c.length ; i > 0 ; i --){         const pIdx = Math.floor(Math.random()*i);         [c[pIdx],c[i-1]] = [c[i - 1],c[pIdx]];         yield c[i-1]     }     return c;} function generate(amount,count){     if(count <= 1) return [amount];     const cards = Array(amount - 1).fill(null).map((_,i) => i+1);     const pick = draw(cards);     const result = [];     for(let i = 0 ; i < count; i++){         result.push(pick.next().value);    }     result.sort((a,b)=>a-b);     for(let i = count - 1 ; i > 0 ; i--){         result[i] = result[i] - result[i - 1];    }     return result; }