5个高级JS技巧


一、前言

JavaScript中,有一些被异常低估的技巧,恰恰我认为这些技巧对于前端朋友们拉高自己代码层次是非常有帮助的,它们分别是FlatMap、数组方法链的顺序、充分使用reduce、充分使用generator、充分使用原生JS类。

二、FlatMap

FlatMap本质上将 mapfilter 数组方法的技术结合在一起(非语法糖)。强烈建议使用 flatMap() 而不是 filter()map() 的组合。

FlatMap只需一次遍历,不会产生中间数组,但 filter()map() 组合会产生中间数组。

2.1、使用 filtermap

接下来我们看一个具体例子:

console.time("filterAndMap")
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const squaredOddNumbers = numbers.filter(num => num % 2 !== 0).map(num => num * num)

console.log(squaredOddNumbers); // [1, 9, 25, 49, 81]
console.timeEnd("filterAndMap")

我们能看到使用 filter + map 的方式使用了 0.31ms

2.2、使用 filterMap

接下来再看看使用 filterMap的性能:

console.time("filterAndMap")
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const squaredOddNumbers = numbers.flatMap(num =>
    num % 2 !== 0 ? [num * num] : []
);


console.log(squaredOddNumbers); // [1, 9, 25, 49, 81]
console.timeEnd("filterAndMap")

使用 filterMap 处理同样的情况只需要 0.22ms

三、数组方法链的排序

JavaScript中一些常见且广受欢迎的数组方法包括 .filter().find().map() 以及 .reduce()。这些方法可以灵活组合,从而创造出一些非常优秀的模式。

// 一个数组方法,仅对奇数进行排序并将其立方
numbers
  .sort((a, b) => a - b)
  .filter((n) => n % 2 !== 0)
  .map((n) => n ** 3);

乍一看,你可能会觉得这段代码写得不错,但你有没有发现代码是先对数字排序,接着再进行筛选。事实上,应该先进行筛选,再进行排序和提高幂次,就能减少一部分没必要的遍历。这样一来,我们就有机会优化由.链接的数组方法。

下面是优化后的代码:

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

// 一个数组方法,仅对奇数进行排序并将其立方
numbers
  .filter((n) => n % 2 !== 0)
  .sort((a, b) => a - b)
  .map((n) => n ** 3);

四、充分使用reduce

让我们来看一个例子,这个例子是关于计算数组中每个元素出现的次数。通常,大多数人可能会选择使用 forEach 或者 for 循环来完成这个任务,但使用 reduce 可以使代码更简洁。

假设我们有一个数组,数组元素如下:

let arr = ['apple', 'banana', 'apple', 'orange', 'banana', 'banana'];

如果我们使用 forEach 来计算每个元素出现的次数,代码可能会是这样的:

let count = {};
arr.forEach(function(i) { count[i] = (count[i]||0) + 1;});

上述代码虽然能够完成任务,但是如果我们使用 reduce,代码会更加简洁且易读:

let count = arr.reduce((prev, cur) => {
  prev[cur] = (prev[cur] || 0) + 1;
  return prev;
}, {});

在这个例子中,reduce 函数接受两个参数,一个是累加器(在这里是一个对象),另一个是当前的元素。我们检查累加器中是否已经有了当前元素作为键,如果有,就将其值加一,如果没有,就设置其值为一。最后,返回累加器对象,它将包含每个元素及其出现的次数。

这个例子清楚地展示了 reduce 的优势,它可以使代码更加简洁且易读,并且提供了一种优雅的方式来处理这种常见的问题。

五、充分使用Generators

生成器的使用场景之一就是处理大量数据的情况,比如在实现无限加载的功能时。让我们通过一个具体的例子来看看如何使用生成器来实现这个功能,并比较其与其他方法的优劣。

首先,我们定义一个异步生成器函数 fetchData,该函数会不断地从某个 API 获取数据。

asyncfunction* fetchData() {
    let pageIndex = 1;
    while (true) {
        const url = `https://fakeapi.com/data?page=${pageIndex}`;
        const res = await fetch(url);
        const data = await res.json();
        yield data;
        pageIndex++;
    }
}

然后,我们可以在需要的时候从这个生成器获取数据。

asyncfunction main() {
    const itr = fetchData();
    // 根据用户交互或其他条件来决定何时获取数据
    console.log(await itr.next());
}

这种方法的优点在于,我们可以根据需要控制数据的获取,而不是一次性获取所有数据。这样可以有效减少内存的使用,并提高程序的响应速度。

相比之下,如果我们使用传统的方法(比如回调或 Promise)来获取数据,那么在数据量大的情况下,我们可能需要一次性获取所有数据,或者使用复杂的逻辑来控制数据的获取。这不仅会增加代码的复杂性,而且可能会导致内存溢出或程序响应缓慢。

因此,对于处理大量数据的场景,使用生成器和迭代器是一种更好的选择。

六、充分使用原生JavaScript

Javascript内置了一些原生的javascript类,可以帮助我们轻松创建/实例化URLHeaders等对象。首先我们看看你是怎么构建URL参数的:

asyncfunction getUrl(userId, limit, category){
  return`https://fakestoreapi.com/products${category ? `/category/${category}` : ""}${limit ? Number(limit):""}${userId? Number(userId):""}`;    
}

上面的代码很混乱,很可能会出错,并且每次添加其他参数时都需要在最后添加一些规则。通过使用原生类如URL,我们可以改进我们的代码。

改进后的代码如下:

function constructURL(category, limit, userId) {
  const baseURL = "https://fakestoreapi.com/products";
  const url = new URL(baseURL);
  const params = new URLSearchParams();

  if (category) url.pathname += `/category/${category}`;
  if (limit) params.append('limit', Number(limit).toString());
  if (userId) params.append('userId', Number(userId).toString());

  url.search = params.toString();
  return url.toString();
}

这样,就可以在同一个函数中处理复杂的URL条件。其中,这里的内置URL对象遵循了 BuilderPattern,它是 JavaScript 许多设计模式的其中之一,可以将复杂逻辑隐藏在一个单独的地方,从而提高可读性。

七、最后

本人每篇文章都是一字一句码出来,希望对大家有所帮助,多提提意见。顺手来个三连击,点赞👍收藏💖关注✨,一起加油☕

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小马甲丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值