加快 JavaScript 执行速度🚀🚀🚀
偷懒
注意对象链式调用
使用转译器前请三思
了解 SMI 和堆编号
评估局部变量
使用 Map 代替 switch / if-else 条件语句
if-else 排序
性格类型是你最好的朋友
其他的
作为开发人员,我们总是想方设法让我们的代码运行得更快、更好。
但在此之前,编写高性能代码需要做到三件事:
- 了解这门语言及其运作方式。
- 基于用例的设计
- 调试!修复!重复!
记住这一点,
任何傻瓜都能写出计算机能理解的代码。优秀的程序员写出的代码,人类也能理解。——马丁·福勒
让我们来看看如何让 JavaScript 代码运行得更快。
偷懒
惰性算法会将计算推迟到需要执行时才执行,然后再产生结果。
const someFn = () => {
doSomeOperation();
return () => {
doExpensiveOperation();
};
}
const t = someArray
.filter(x => checkSomeCondition(x))
.map(x => someFn(x));
// Now execute the expensive operation only when needed.
t.map(x => t());
简而言之:最快的代码就是不执行的代码。所以,尽量延迟执行代码。
注意对象链式调用
JavaScript 使用原型继承。JavaScript 世界中的所有对象都是Object的实例。
MDN表示,
当尝试访问对象的属性时,不仅会在该对象上查找该属性,还会在该对象的原型上查找,在原型的原型上查找,依此类推,直到找到名称匹配的属性或到达原型链的末尾。
对于每个属性,JavaScript 引擎都必须遍历整个对象链,直到找到匹配项。如果使用不当,这会消耗大量资源并严重影响应用程序性能。
所以不要这样做
const name = userResponse.data.user.firstname + userResponse.data.user.lastname;
而是这样做
const user = userResponse.data.user;
const name = user.firstname + user.lastname;
简而言之:使用临时变量来保存链式属性,而不是重复遍历整个链式结构。
使用转译器前请三思
在上述情况下,该对象userResponse可能具有也可能不具有该data对象。该data对象可能具有也可能不具有该user属性。
我们可以像这样检查获取值的过程
let name = '';
if (userResponse) {
const data = userResponse.data;
if (data && data.user) {
const user = data.user;
if (user.firstname) {
name += user.firstname;
}
if (user.lastname) {
name += user.firstname;
}
}
}
这段代码确实有点冗长。代码越多,出错的可能性就越大。我们能不能精简一下呢?当然可以,JavaScript 有可选链式调用和解构赋值等机制来简化代码。
const user = userResponse?.data?.user;
const {firstname = '', lastname = ''} = user;
const name = firstname + lastname;
是不是很酷炫?很现代?但是使用这类东西时要小心,Babel 会将它们转译成如下形式:
"use strict";
var _userResponse, _userResponse$data;
var user = (_userResponse = userResponse) === null || _userResponse === void 0 ? void 0 : (_userResponse$data = _userResponse.data) === null || _userResponse$data === void 0 ? void 0 : _userResponse$data.user;
var _user$firstname = user.firstname,
firstname = _user$firstname === void 0 ? '' : _user$firstname,
_user$lastname = user.lastname,
lastname = _user$lastname === void 0 ? '' : _user$lastname;
var name = firstname + lastname;
简而言之:使用转译时,请确保选择最适合您用例的转译方式。
了解 SMI 和堆编号
数字很奇妙。ECMAScript 将数字标准化为 64 位浮点值,也称为 64 位浮点double precision floating-point表示Float64法。
如果 JavaScript 引擎以 Float64 格式存储数字,将会导致严重的性能损失。JavaScript 引擎对数字进行抽象,使其行为与 Float64 完全一致。与 Float64 运算相比,JavaScript 引擎执行整数运算的速度要快得多float64。
更多详情请见此处。
TL;DR:尽可能使用小整数(SMI)。
评估局部变量
有时,人们认为提供这样的值是可行的,
const maxWidth = '1000';
const minWidth = '100';
const margin = '10';
getWidth = () => ({
maxWidth: maxWidth - (margin * 2),
minWidth: minWidth - (margin * 2),
});
如果getWidth函数被多次调用,每次调用时都会重新计算该值。上述计算量不大,因此不会对性能造成任何影响。
但总的来说,运行时评估越少,性能越好。
// maxWidth - (margin * 2)
const maxWidth = '980';
// minWidth - (margin * 2)
const minWidth = '80';
const margin = '10';
getWidth = () => ({
maxWidth,
minWidth
});
运行时评估次数越少,性能越好。
使用 Map 代替 switch / if-else 条件语句
当你想检查多个条件时,请使用 `a`Map而不是switch`/`if-else条件。在 `a` 中查找元素的性能map远高于评估 ` switchand`if-else条件。
switch (day) {
case 'monday' : return 'workday';
case 'tuesday' : return 'workday';
case 'wednesday' : return 'workday';
case 'thursday' : return 'workday';
case 'friday' : return 'workday';
case 'saturday' : return 'funday';
case 'sunday' : return 'funday';
}
// or this
if (day === 'monday' || day === 'tuesday' || day === 'wednesday' || day === 'thursday' || day === 'friday') return 'workday';
else return 'funday';
两者都用这个,
const m = new Map([
['monday','workday'],
['tuesday', 'workday'],
['wednesday', 'workday'],
['thursday', 'workday'],
['friday', 'workday'],
['saturday', 'funday'],
['sunday', 'funday']
];
return m.get(day);
TL;DR;使用 Map 代替 switch/if-else 条件语句
if-else 排序
例如,如果您正在编写 React 组件,遵循这种模式非常常见。
export default function UserList(props) {
const {users} = props;
if (users.length) {
// some resource intensive operation.
return <UserList />;
}
return <EmptyUserList />;
}
这里,我们会<EmptyUserList />在没有用户时渲染页面<UserList />。我看到有人认为我们应该先处理所有负面情况,然后再处理正面情况。他们经常提出这样的论点:这种写法更清晰易懂,效率也更高。也就是说,下面的代码比之前的代码效率更高。
export default function UserList(props) {
const {users} = props;
if (!users.length) {
return <EmptyUserList />;
}
// some resource intensive operation
return <UserList />;
}
但如果条件users.length始终为真呢?先使用这个条件,然后再使用否定条件。
简而言之:在设计
if-else条件时,应按减少评估条件数量的顺序排列。
性格类型是你最好的朋友
JavaScript 既是解释型语言又是编译型语言。编译器为了生成更高效的二进制文件,需要类型信息。但由于 JavaScript 是一种动态类型语言,这给编译器带来了困难。
编译器在编译高频代码(即执行次数较多的代码)时,会做出一些假设并进行优化。编译器需要花费一些时间来生成这些优化后的代码。当这些假设不成立时,编译器就必须丢弃优化后的代码,并回退到解释执行的方式。这既耗时又费力。
简而言之:始终使用单态类型。
其他的
尽量避免使用递归,虽然递归很棒,代码也更易读,但也会降低性能。
尽可能地使用记忆化功能。
有时,位运算符和一元运算符能略微提升性能。但当性能预算非常紧张时,它们才真正发挥作用。
讨论区🐦 Twitter // 💻 GitHub // ✍️ 博客
如果你喜欢这篇文章,请点赞或留言。❤️
文章来源:https://dev.to/sendilkumarn/boost-your-javascript-performance-2fbl