[前端]JS学习(2)

本文详细探讨了JavaScript代码优化实例,涉及单位矩阵快速判断、交通灯状态切换的不同版本,从性能、代码风格和可维护性角度进行了深入剖析。同时介绍了如何正确判断4的幂和高效洗牌算法,以及分发红包的两种方案对比。
摘要由CSDN通过智能技术生成


学习JS(2)

从代码本身写法,解决问题上看。

看一段代码好不好

//判断一个mat2d矩阵是否是单位矩阵
function isUnitMatrix2d(m){
	return m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0;
}

依次判断六个点,没有用循环,在图形渲染中,调用多,因此性能最好。

 get layerTransformInvert() {
    if(this[_layerTransformInvert]) return this[_layerTransformInvert];
    const m = this.transformMatrix;
    if(m[0] === 1 && m[1] === 0 && m[2] === 0 && m[3] === 1 && m[4] === 0 && m[5] === 0) {
      return null;
    }
    this[_layerTransformInvert] = mat2d.invert(m);
    return this[_layerTransformInvert];
  }

这是一段真实的代码https://github.com/spritejs/spritejs/blob/master/src/node/layer.js#L131

每一帧都需要判断,用最快的方式。

需要看使用场景,关注什么

风格;效率;约定;使用场景;设计;…

当年的 Left-pad 事件

用来补齐字符串位数模块,后来被作者下了,很多依赖它的常见的npm库用不了了。

在这里插入图片描述

事件本身的槽点:

NPM模块粒度;(这么小的功能模块化)

代码风格;

代码质量/效率.

考虑了String转换,判断占位符,while循环补占位符,

二次幂的方法补齐性能更好,用位数循环O(logn)复杂度,代码更简洁,效率提升;

运用repeat函数。

在这里插入图片描述

repeat:

在这里插入图片描述

结构:

在这里插入图片描述

再优化,与它的-1进行&=运算,二进制数1的个数有几个循环几次,性能更好。

需要补的位数不是很大,没有必要性能提升。(Repeat polyfill / MDNhttps://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat

不做过度的优化和设计,增加复杂度。

交通灯-状态切换

异步

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

版本一(不好)

用多个setTimeout嵌套,但是异步函数嵌套多了会有回调的问题。

如果用setInterval的话,时间不一样不可以用一个setInterval来实现。

数据抽象也不好。

在这里插入图片描述

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);
})();
版本二(比较好)

数据抽象,数据封装,用start方法。

如果要添加新的数据修改stateList即可。

在这里插入图片描述

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);
版本三

过程抽象,循环;将循环过程写成setState方法当作主体;可以将traffic封装进去。不太容易理解

在这里插入图片描述

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();
  }
}());
版本四(√)

更符合直觉的思路,既优雅又不违背直觉和思维习惯。

setState/wait交替执行,set是瞬间执行的,wait是异步执行的。

在这里插入图片描述

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();
代码

HTML

<ul id="traffic" class="wait">
  <li></li>
  <li></li>
  <li></li>
</ul>

CSS

#traffic {
  display: flex;
  flex-direction: column;
}

#traffic li{
  display: inline-block;
  width: 50px;
  height: 50px;
  background-color: gray;
  margin: 5px;
  border-radius: 50%;
}

#traffic.stop li:nth-child(1) {
  background-color: #a00;
}

#traffic.wait li:nth-child(2) {
  background-color: #aa0;
}

#traffic.pass li:nth-child(3) {
  background-color: #0a0;
}

判断是否是4的幂

版本一

在这里插入图片描述

如果是4的幂可以不断被4整除,时间复杂度为logn。

版本二

在这里插入图片描述

优化:直接右移两位,不用判断是否能被4整除,如果能被4整除最后两位为00,因此能用11&来判断。

版本三

在这里插入图片描述

js有效精度的整数53位。

时间复杂度从logn降到常数。

有效精度的整数有限,如果是4的幂,转为二进制后的数最高位是1,后面都是0,并且是偶数个0(跟1010&,考虑精度:0xAAAAAAAA)。

版本四

在这里插入图片描述

转换为字符串,用正则表达式判断。转为字符串不会太长,性能也没问题。

在这里插入图片描述

在这里插入图片描述

洗牌

数组随机次序

不正确版本

在这里插入图片描述

上面的例子事实上统计发现,越小的数出现在前面的概率越高,越大的数出现在后面的概率越高。

这个算法不是一个等概率分布的算法,前后有一半的概率交换,但是还是前面的数倾向于在前面。

在这里插入图片描述

正确性验证:

将上述方法执行一百万次,将各数分别相加,发现整体越往后的数越大。

在这里插入图片描述

因此这个方法不正确。

实现版本

洗牌算法要保证正确性,每次随机从剩下的牌中挑出一个,第一次从n张牌中随机抽一张放在第一位,第二次从剩下n - 1 张牌中随机抽一张放在第二位,……

时间复杂度O(n)的算法

在这里插入图片描述

也进行一百万次的结果相加,可以看出每一位基本可以看做数值相等,符合预期。

抽牌

在这里插入图片描述

不用全部洗一遍,只抽取少量的牌,计算量小,性能更高。

分红包

版本一

每一次选大的红包进行切分,相对来说比较平均,差别不大。

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;
}
版本二

随机选择n - 1个数分成n段。

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

抽牌思路相似

在这里插入图片描述


总结

这里对文章进行总结:
以上就是今天的笔记内容,本文仅仅简单从代码上通过几个例子讨论JS的写法,更多有关前端的知识参考后续文章。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值