五个javascript开发小技巧,助你按时下班~

886 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

1. 随机数/随机 id

function getUuid(len = 32) {
  const S4 = () => Math.random().toString(16).substring(2);
  let res = "";
  while (res.length < len) {
    res += S4().substring(0, len - res.length);
  }
  return res;
}

代码分析

  • Math.random().toString(16) 获取随机数,转成 16 进制
  • substring(2)前两位是 0.所以从第二位之后开始取
  • while (res.length < len) 循环,直到取到符合长度要求的字符串
  • substring(0, len - res.length),这一步也很关键,特别是最后一次循环,确保不会多截取

2. 获取文本确切宽度

function getActualWidthOfChars(text, options = {}) {
  const { size = 14, family = "Microsoft YaHei" } = options;
  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");
  ctx.font = `${size}px ${family}`;
  const metrics = ctx.measureText(text);
  return Math.abs(metrics.actualBoundingBoxLeft) + Math.abs(metrics.actualBoundingBoxRight);
}

代码分析

将文本绘制到 canvas 上,再使用 canvas 的measureTextapi 获取物理宽度。具体可参考我的上篇文章面试官:你是如何获取文本宽度的?

3. 判断某个对象是否发生变化

  1. 可以通过序列化的方式 JSON.stringify(objA) === JSON.stringify(_objA)

    页面未保存触发拦截的文章中有使用到这个方法,判断表单内容是否有修改未保存 可以参考这篇文章如何实现页面跳转拦截?

  2. 更多情况下我们不需要判断整个对象是否变化,而是对象里面的某个属性

这里直接再复杂一丢丢,判断数组里面每个属性是否发生变化

equalProps(newV, oldV, props) {
    if (!newV || !oldV || newV.length !== oldV.length) return false;
    return newV.every((e, i) => JSON.stringify(e[props]) === JSON.stringify(oldV[i][props]));
}

代码分析

  1. 首先如果newV, oldV有一个为 null 或 undefined 或空,或者二者长度不一致,直接返回 false,props一定发生了变化
  2. 循环遍历newV, oldV,只需要遍历一遍,通过i下标访问另一个,序列化方式比较是否相等
  3. every 确保只要找到一个不满足判断条件的情况,立即终止。(也可以选择用 some)

我这里有个实际的例子,帮助大家体会下:

watch: {
  cardList: {
    handler: function (newV, oldV) {
      const equal = this.equalProps(newV, oldV, 'operators');
      // 如果operators发生了变化,重绘缩略图
      !equal && this.initImagesMap();
    },
    immediate: true,
    deep: true,
  }
}

功能描述: 监听cardList的变化更新,重绘card 上的缩略图。

但是缩略图的数据存在于operators上,当我们修改cardList上的其他属性,因为是 deep,所以也会触发缩略图的重绘。 我们现在加了个收藏的属性,用户点击收藏图标,发现这个 所有card上的缩略图都重刷了一遍,体验极差。

用上equalProps方法之后,就可以避免其他属性变化对缩略图的影响~

还有其他好办法吗?? 我本寄希望于 Lodash,文档翻了一遍没找到对应的方法。 大家如果有更好的方式,可以在评论区交流。

4. 巧用sort()调取目标元素到队首/位

先看一下我们常用的数组排序:

[4,3,9,5,2].sort(), ['b', 'a', 'c'].sort()

数字根据大小排序,字符串根据 unicode 码排序。

这些最基础的用法相信大家都比较熟悉了,不做赘述。

根据 MDN 的描述,其实 sort 是接收一个回调函数(a, b) => {}, 回调返回值 等于 0: 顺序不变; 小于零:a 在前 b 在后, 大于 0:b 在前 a 在后;。

【这里可以记住 sort()不传参数等同于 (a,b) => a - b;是从小到大排序的,帮助记忆】

[4,3,9,5,2].sort((a,b) => a - b), a, b分别代表迭代的前一项和后一项。

基于以上,如何实现下面的需求:

image.png

这样的一个组件,需要向后端传递一个数组,当前选中的属性在前,折叠在下面的属性在后,并且每一项上包含该属性是正序还是逆序排列的属性。

数据结构长这样:

const ORDERLIST = [
  {
    label: "热度",
    value: "HEAT",
    order: "DESC",
  },
  {
    label: "更新时间",
    value: "UPDATE_TIME",
    order: "DESC",
  },
];

没错,把当前展示的属性移动到上面就可以了。

具体要怎么做呢?

从两项中查找目标并删除,然后再插入到数组前面? 能实现但显然不够优雅~

我们试着用 sort 来实现下:

ORDERLIST.map(e => ...).sort((a, b) => a.value === currentProp ? -1 : 1)

当 当前项等于目标值时,返回 -1, 小于零, a 会排在 b 的前面。

在每次两两比较的时候,目标对象都会排在另一个前面,也就意味着它一定会排在数组的第一个。(可以想下冒泡排序的原理)

所以即使数组存在很多项, 此方法也同样适用哦~

比如:

image.png

我们在下拉项目列表里面将当前项目提到第一个,刚好就可以用上面的排序方式!美滋滋~

5. 巧用 reduce 减少遍历次数

首先我们有这样的一份数据,要求从中筛选出包含 id 的菜单项并组合成数组返回。

const modules = {
  modulesA: {
    name: "modelA_name",
    menus: [
      {
        id: "menu-A1",
        name: "菜单项A-1",
      },
      {
        name: "菜单项A-2",
      },
    ],
  },
  modulesB: {
    name: "modelB_name",
    menus: [
      {
        id: "menu-B1",
        name: "菜单项B-1",
      },
    ],
  },
};

同事的原始代码:

function allTasks() {
  const _modules = Object.keys(modules).filter(
    key => modules[key].menus && modules[key].menus.some(i => i.id)
  );
  let tasks = [];
  _modules.forEach(key => {
    tasks = tasks.concat(
      modules[key].menus
        .filter(e => !!e.id)
        .map(e => {
          return {
            ...e,
            moduleCode: key,
          };
        })
    );
  });
  return tasks;
}

reduce 优化后的代码:

function allTasks() {
  return Object.keys(modules).reduce((res, key) => {
    let item = [];
    if (modules[key].menus && modules[key].menus.some(i => i.id)) {
      item.push(
        ...modules[key].menus
          .filter(e => !!e.id)
          .map(e => {
            return {
              ...e,
              moduleCode: key,
            };
          })
      );
    }
    return res.concat(item);
  }, []);
}

我们成功从两次遍历缩减为了一次遍历,具体的细节就不展开了,仔细看下,还是比较好理解的。

总结

以上就是笔者在项目中经常使用的一些 javascript 小技巧,掌握这些技巧,确实能够在一定程度上提高开发效率。助力前端开发正常下班~

image.png

一抬头快十二点了,抓紧收尾睡觉了~

更文不易,如果对你有帮助还请点个赞支持下,帮助笔者输出更多优质文章哦~