编写更快的 JavaScript
一些背景信息
尽可能使用集合而不是数组
概括
我们写的代码很多时候都是从互联网上复制粘贴过来的。如今,StackOverflow是解决各种问题的主要来源。但是,在不了解幕后运作的情况下,盲目地复制粘贴代码真的可以吗?
一些背景信息
我说StackOverflow不应该盲目使用,别误会。它对于全球开发者日常遇到的大多数问题和 bug 来说,都是一个很好的信息来源。只是我们应该更积极主动一些,从所有可用的选项中,找到最佳的解决方案。
让我向您展示一些例子,其中一段代码可以用多种方式编写,最明显的选择不一定是最好的选择。
链接数组循环链接
假设我们有一个200,000
包含 name 和 age 属性的对象数组。我们想获取所有 22 岁以下的人的姓名(假设有100,000
22 个人)。大多数人可能会使用以下解决方案:
const under22People = originalList
.filter(x => x.age < 22)
.map(x => x.name)
自从ES5
和ES6
被引入以来,出现了一组优秀的数组方法。ES6s
更简洁的语法使得我们可以轻松地链接方法以产生所需的结果。问题在于我们如何使用这些优秀的方法,却没有意识到它们是如何执行的。
乍一看,这似乎是一段非常好的代码,但如果仔细观察,就会发现有一个循环,它200,000
在进行过滤时运行一次,100,000
在选择名称时运行另一次。
到这里我们只需要循环遍历这些项一次。那么我们来看看如何重写这段代码:
const under22People = []
originalList.forEach(({ age, name }) => {
age >= 18 && under22People.push(name)
})
现在这段代码只运行了200,000
一次。它看起来不如链式方法那么美观,但性能确实好得多。
您甚至可以使用该reduce
方法做同样的事情:
const under22People = originalList.reduce(
(acc, { age, name }) => {
return age >= 18 ? [...ac, name] : acc
},
[]
)
它确实看起来可读性较差,但它对我们来说完成了完全相同的工作。
滥用箭头函数
在写作时你可以问自己的一个很好的问题JavaScript
是,你是否知道传统函数和箭头函数(又名胖函数)之间的区别。
来自 MDN Web 文档:
箭头函数表达式是常规函数表达式的一种语法紧凑的替代方案,尽管它本身没有与
this
、arguments
、super
或new.target
关键字的绑定。箭头函数表达式不适合用作方法,也不能用作构造函数。
在你问之前,它们并不等同于 JavaScript 中诸如 之类的语言中的匿名函数C#
。但现在你唯一需要关心的是它们没有作用域。
它们的许多用例之一是在类字段中使用它们。考虑一下React
你不再需要手动绑定你的函数,例如:
this.handleClick = this.handleClick.bind(this)
相反,你会像这样编写你的类:
class MyComponent extends Component {
handleClick = () => {
// ...
}
render() {
// ...
}
}
您可能知道,通常的函数定义在原型中,并在所有实例之间共享。如果我们有一个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)
}
上述代码的问题在于,每次调用 时doSomething
,nestedHeler
都会重新创建。为了避免这种情况,您可以使用IIFE
(立即调用函数):
const result = (function() {
function privateHelper(arg) {
var result = process(arg)
return result
}
return function(arg1, arg2) {
return (
privateHelper(arg1) + privateHelper(arg2)
)
}
})()
当执行此代码时,嵌套方法将仅创建一次🤷♂️。
尽可能使用集合而不是数组
array
an和 a之间最明显的区别Set
是它array
是一个索引集合,而 aSet
是基于键的。
那么为什么你应该使用Set
?
搜索项目
使用indexOf()
或includes()
来检查数组中是否存在某个元素比较慢。在 a 中,Set
你可以使用 来非常轻松地找到某个元素has()
:
const mySet = new Set([1, 1, 2])
console.log(mySet.has(2)) // true
删除项目
在 中Set
,你可以通过其值删除一个元素。在数组中,等效的操作是splice()
根据元素的 进行操作index
。与上一点一样,依赖索引会比较慢。
const mySet = new Set([1, 2, 3, 4, 5])
mySet.delete(1)
插入项目
使用或将项目添加到Set
比添加到数组要快得多。push()
unshift()
const mySet = new Set([1, 2])
mySet.add(3) // Successfully added
存储 NaN
您不能使用indexOf()
来查找值NaN
,但 aSet
可以存储该值。
删除重复项
Set
对象只存储唯一值。如果你想避免存储重复值,这比数组有显著的优势,因为数组需要额外的代码来处理重复值。
const mySet = new Set([1, 1, 2])
mySet.add(3) // Successfully added
console.log(mySet.values()) // 1,2,3
概括
有很多这样的例子,在实际的商业应用程序中编写代码时,你可能需要小心谨慎。速度是每个 Web 应用程序最重要的部分之一,考虑以上几点,将提高代码的性能,从而让用户更满意。
希望这能帮助您开始思考还有哪些其他方法可以解决同一问题,而不是只使用您在网上找到的第一个解决方案。
文章来源:https://dev.to/yashints/write-faster-javascript-18f7