编写更快的 JavaScript 一些背景知识尽可能使用集合而不是数组摘要

2025-06-07

编写更快的 JavaScript

一些背景信息

尽可能使用集合而不是数组

概括

我们写的代码很多时候都是从互联网上复制粘贴过来的。如今,StackOverflow是解决各种问题的主要来源。但是,在不了解幕后运作的情况下,盲目地复制粘贴代码真的可以吗?

一些背景信息

我说StackOverflow不应该盲目使用,别误会。它对于全球开发者日常遇到的大多数问题和 bug 来说,都是一个很好的信息来源。只是我们应该更积极主动一些,从所有可用的选项中,找到最佳的解决方案。

让我向您展示一些例子,其中一段代码可以用多种方式编写,最明显的选择不一定是最好的选择。

链接数组循环链接

假设我们有一个200,000包含 name 和 age 属性的对象数组。我们想获取所有 22 岁以下的人的姓名(假设有100,00022 个人)。大多数人可能会使用以下解决方案:

const under22People = originalList
  .filter(x => x.age < 22)
  .map(x => x.name)
Enter fullscreen mode Exit fullscreen mode

自从ES5ES6被引入以来,出现了一组优秀的数组方法。ES6s更简洁的语法使得我们可以轻松地链接方法以产生所需的结果。问题在于我们如何使用这些优秀的方法,却没有意识到它们是如何执行的。

乍一看,这似乎是一段非常好的代码,但如果仔细观察,就会发现有一个循环,它200,000在进行过滤时运行一次,100,000在选择名称时运行另一次。

到这里我们只需要循环遍历这些项一次。那么我们来看看如何重写这段代码:

const under22People = []
originalList.forEach(({ age, name }) => {
  age >= 18 && under22People.push(name)
})
Enter fullscreen mode Exit fullscreen mode

现在这段代码只运行了200,000一次。它看起来不如链式方法那么美观,但性能确实好得多。

您甚至可以使用该reduce方法做同样的事情:

const under22People = originalList.reduce(
  (acc, { age, name }) => {
    return age >= 18 ? [...ac, name] : acc
  },
  []
)
Enter fullscreen mode Exit fullscreen mode

它确实看起来可读性较差,但它对我们来说完成了完全相同的工作。

滥用箭头函数

在写作时你可以问自己的一个很好的问题JavaScript是,你是否知道传统函数和箭头函数(又名胖函数)之间的区别。

来自 MDN Web 文档:

箭头函数表达式是常规函数表达式的一种语法紧凑的替代方案,尽管它本身没有与thisargumentssupernew.target关键字的绑定。箭头函数表达式不适合用作方法,也不能用作构造函数。

在你问之前,它们并不等同于 JavaScript 中诸如 之类的语言中的匿名函数C#。但现在你唯一需要关心的是它们没有作用域。

它们的许多用例之一是在类字段中使用它们。考虑一下React你不再需要手动绑定你的函数,例如:

this.handleClick = this.handleClick.bind(this)
Enter fullscreen mode Exit fullscreen mode

相反,你会像这样编写你的类:

class MyComponent extends Component {
  handleClick = () => {
    // ...
  }

  render() {
    // ...
  }
}
Enter fullscreen mode Exit fullscreen mode

您可能知道,通常的函数定义在原型中,并在所有实例之间共享。如果我们有一个N组件列表,这些组件将共享相同的方法。因此,如果我们的组件被点击,我们仍然会调用N多次方法,但它会调用同一个原型。由于我们在原型中多次调用相同的方法,JavaScript引擎可以对其进行优化。

另一方面,对于类属性中的箭头函数,如果我们创建N组件,这些N组件也会创建N函数。通过查看转译版本可以看到,类属性是在构造函数中初始化的。这意味着如果我们点击N组件,N就会调用不同的函数。

因此,下次编写新的闪亮React组件时,请考虑这种方法。

嵌套函数 VS IIFE

将一个函数嵌套在另一个函数中似乎是一个好主意,可以将一些逻辑与外部世界隔离开来。考虑下面的代码:

function doSomething(arg1, arg2) {
  function nestedHelper(arg) {
    return process(arg)
  }

  return nestedHelper(arg1) + nestedHelper(arg2)
}
Enter fullscreen mode Exit fullscreen mode

上述代码的问题在于,每次调用 时doSomethingnestedHeler都会重新创建。为了避免这种情况,您可以使用IIFE(立即调用函数):

const result = (function() {
  function privateHelper(arg) {
    var result = process(arg)
    return result
  }

  return function(arg1, arg2) {
    return (
      privateHelper(arg1) + privateHelper(arg2)
    )
  }
})()
Enter fullscreen mode Exit fullscreen mode

当执行此代码时,嵌套方法将仅创建一次🤷‍♂️。

尽可能使用集合而不是数组

arrayan和 a之间最明显的区别Set是它array是一个索引集合,而 aSet是基于键的。

那么为什么你应该使用Set

搜索项目

使用indexOf()includes()来检查数组中是否存在某个元素比较慢。在 a 中,Set你可以使用 来非常轻松地找到某个元素has()

const mySet = new Set([1, 1, 2])

console.log(mySet.has(2)) // true
Enter fullscreen mode Exit fullscreen mode

删除项目

在 中Set,你可以通过其值删除一个元素。在数组中,等效的操作是splice()根据元素的 进行操作index。与上一点一样,依赖索引会比较慢。

const mySet = new Set([1, 2, 3, 4, 5])

mySet.delete(1)
Enter fullscreen mode Exit fullscreen mode

插入项目

使用或将项目添加到Set比添加到数组要快得多push()unshift()

const mySet = new Set([1, 2])

mySet.add(3) // Successfully added
Enter fullscreen mode Exit fullscreen mode

存储 NaN

您不能使用indexOf()来查找值NaN,但 aSet可以存储该值。

删除重复项

Set对象只存储唯一值。如果你想避免存储重复值,这比数组有显著的优势,因为数组需要额外的代码来处理重复值。

const mySet = new Set([1, 1, 2])

mySet.add(3) // Successfully added

console.log(mySet.values()) // 1,2,3
Enter fullscreen mode Exit fullscreen mode

概括

有很多这样的例子,在实际的商业应用程序中编写代码时,你可能需要小心谨慎。速度是每个 Web 应用程序最重要的部分之一,考虑以上几点,将提高代码的性能,从而让用户更满意。

希望这能帮助您开始思考还有哪些其他方法可以解决同一问题,而不是只使用您在网上找到的第一个解决方案。

文章来源:https://dev.to/yashints/write-faster-javascript-18f7
PREV
优化 React 应用
NEXT
展现你的领导能力,即使你没有领导一个团队