【青训营】- 如何写好JS(下)

920 阅读4分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

本文同时参与掘力星计划,赢取创作大礼包,挑战创作激励金

前言

前期回顾

如何写好JavaScript肯定是每一个前端工程师一直以来要思考的问题,老师告诉我们一些写好JavaScript的原则,同时也教了一些我们如何写好JavaScript的技巧,今天来继续跟着老师学JavaScript吧!

这是一段真实的代码

QQ截图20211002230524.png

github.com/spritejs/sp…

其实这是spriteJs 框架的一段真实的代码,可以翻到131行看一下! QQ截图20211002225907.png 其实这是spriteJs 框架的一段真实的代码,可以翻到131行看一下 spriteJs 是一款由 360 奇舞团开源的跨终端 canvas 绘图框架,可以基于 canvas 快速绘制结构化 UI、动画和交互效果,是跨平台的2D绘图对象模型库,它能够支持web、node、桌面应用和微信小程序的图形绘制和实现各种动画效果。

写代码最关注什么?

QQ截图20211002230320.png

当年的Left-pad事件

function leftpad(str, len, ch){
    str = String(str);
    
    var i = -1;
    
    if(!ch && ch !== 0) ch = "";
    
    len = len - str.length;
    
    while(++i < len){
        str = ch + str;
    }
    
    return str;
}

事件本身有很多槽点

  • NPM 模块粒度

  • 代码风格

  • 代码质量/效率

QQ截图20211002231855.png

QQ截图20211002232057.png

QQ截图20211002232354.png

  • 优化一下

QQ截图20211002232359.png

交通灯:状态切换

实现一个切换多个交通灯状态切换的功能

QQ截图20211002232556.png

版本一

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);
})();

QQ截图20211002234303.png

版本二

const traffic = document.getElementById('traffic');

const stateList = [
  {state: 'wait', last: 1000},
  {state: 'stop', last: 3000},
  {state: 'pass', last: 3000},
];

function start(traffic, stateList){
  function applyState(stateIdx) {
    const {state, last} = stateList[stateIdx];
    traffic.className = state;
    setTimeout(() => {
      applyState((stateIdx + 1) % stateList.length);
    }, last)
  }
  applyState(0);
}

start(traffic, stateList);

版本三

const traffic = document.getElementById('traffic');

function wait(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

function poll(...fnList){
  let stateIndex = 0;
  
  return async function(...args){
    let fn = fnList[stateIndex++ % fnList.length];
    return await fn.apply(this, args);
  }
}

async function setState(state, ms){
  traffic.className = state;
  await wait(ms);
}

let trafficStatePoll = poll(setState.bind(null, 'wait', 1000),
                            setState.bind(null, 'stop', 3000),
                            setState.bind(null, 'pass', 3000));

(async function() {
  // noprotect
  while(1) {
    await trafficStatePoll();
  }
}());

版本四

const traffic = document.getElementById('traffic');

function wait(time){
  return new Promise(resolve => setTimeout(resolve, time));
}

function setState(state){
  traffic.className = state;
}

async function start(){
  //noprotect
  while(1){
    setState('wait');
    await wait(1000);
    setState('stop');
    await wait(3000);
    setState('pass');
    await wait(3000);
  }
}

start();

判断是否是4的幂

QQ截图20211002233844.png

版本一

function isPowerOfFour(num) {
  num = parseInt(num);
  while(num > 1) {
    //不能被4整除的话,返回false
    if(num % 4) return false;
    num /= 4;
  }
  return true;
}

版本二

function isPowerOfFour(num) {
  num = parseInt(num);
  while(num > 1) {
    //判断二进制数的末两位,是00的话就是4的倍数
    if(num & 0b11) return false;
    //num/4就相当于右移两位
    num >>>= 2;
  }
  return true;
}

版本三

function isPowerOfFour(num) {
  num = parseInt(num);
  return num > 0 &&
    //num与num-1按位相与得到0,这是2的幂
    (num & (num -1)) === 0 && 
    //且没有奇数个0,只有偶数个0
    (num & 0xAAAAAAAA) === 0;
}

版本四

function isPowerOfFour(num) {
  //转换成一个二进制的字符串
  num = parseInt(num).toString(2);
  //然后用正则表达式匹配一下
  return /^1(?:00)*$/.test(num);
}

洗牌

const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function shuffle(cards) {
  return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}

console.log(shuffle(cards));

QQ截图20211003001417.png

正确性

const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function shuffle(cards) {
  return [...cards].sort(() => Math.random() > 0.5 ? -1 : 1);
}

const result = Array(10).fill(0);

for(let i = 0; i < 1000000; i++) {
  const c = shuffle(cards);
  for(let j = 0; j < 10; j++) {
    result[j] += c[j];
  }
}

console.log(result);

QQ截图20211003001645.png

版本一

每次都随机抽出一张牌,把它换到最后的位置去。 QQ截图20211002235723.png

const cards = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

function shuffle(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]];
  }
  return c;
}

const result = Array(10).fill(0);

for(let i = 0; i < 10000; i++) {
  const c = shuffle(cards);
  for(let j = 0; j < 10; j++) {
    result[j] += c[j];
  }
}

console.log(shuffle(cards));
console.log(result);

QQ截图20211003001002.png

版本二

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];
  }
}

const result = draw(cards);
console.log([...result]);

QQ截图20211003001113.png

分红包

QQ截图20211003000015.png

版本一

每次将金额分成两部分,总会有大的部分和小的部分,我们挑选出最大的一块进行划分,然后将目前的部分再挑出最大的一块进行划分。这个方法分出的红包相对均匀。

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

版本二

假设将100元切9刀,有9999个位置去切分,则这个问题可以转换成洗牌问题,切分位置从1号到9999号,然后将这9999个数随机洗牌,然后分出10个位置,就切出来了。后面位置-前面位置得到切分出的大小。

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];
  }
}

function generate(amount, count){
  if(count <= 1) return [amount];
  const cards = Array(amount - 1).fill(0).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;
}

结语

  • 如以上有错误的地方,请在评论区中指出,谢谢! 小可爱们看完点个赞再走一走~~