1. 使用 FlatMap
在 JavaScript 中,Flat Map 是一种很棒的技术,你可以在这里了解。FlatMap 本质上将 map 和 filter 数组方法的技术结合成了一种。我建议你使用 flatMap() 而不是 filter() 和 map() 的组合。
FlatMap 只需单次遍历,并且不会产生中间数组,而 filter() 和 map() 的组合会产生中间数组。
// 使用 filterAndMap
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")
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")
2. 数组方法的顺序
数组方法是帮助我们与数组交互的一些最重要的方法。JavaScript 中有许多数组方法。最流行的数组方法包括 .filter(), .find(), .map(), .reduce()。它们可以合并在一起产生一些很棒的模式,就像这样
// 一个仅对数组进行排序的数组方法
// 仅适用于奇数,并将其提高到3的幂次方
numbers
.sort((a, b) => a - b)
.filter((n) => n % 2 !== 0)
.map((n) => n ** 3);
乍一看,上面的程序看起来很好,对吧,但是这里有一个大问题。注意我们是如何首先对数字进行排序,然后再进行过滤的。如果我们先使用 filter 再进行排序和提高幂次方,我们可以做更少的任务。这样,我们可以优化一组由(.)链接的数组方法。
上面的最佳代码为
const numbers = [9, 3, 6, 4, 8, 1, 2, 5, 7];
// 一个仅对数组进行排序的数组方法
// 仅适用于奇数,并将其提高到3的幂次方
numbers
.filter((n) => n % 2 !== 0)
.sort((a, b) => a - b)
.map((n) => n ** 3);
3. 使用 reduce。
我发现很多前端开发者都有这个问题。例如,像 react-charts 这样的包要求以对象结构提供数据,但是 react-charts 的实现要求以按键分组的格式提供数据,因此我看到大多数开发者使用 .forEach() 方法或不正确地使用 map() 方法,就像这样
fetch("https://jsonplaceholder.typicode.com/todos/")
.then(res=>res.json())
.then(todos=>{
// 使用 Map
const todosForUserMap = {};
todos.forEach(todo=>{
if (todosForUserMap[todo.userId]){
todosForUserMap[todo.userId].push(todo);
}else{
todosForUserMap[todo.userId] = [todo];
}
})
console.log(todosForUserMap)
})
这种方法很好,因为它使用了 forEach 方法而不是 map 方法。在这里,显然不能使用 map 方法,因为数组将为每个元素构建数组。假设数组有1000个条目,那么将会为每个元素创建一个包含1000个条目的空数组,这个数组创建不会在 forEach() 中发生。
与上述任何方法都不同,一个更干净、更可读的方法是使用 Array reduce 方法 ,上面的代码现在被更正为
fetch("https://jsonplaceholder.typicode.com/todos/")
.then(res=>res.json())
.then(todos=>{
// 使用 Map
const todosForUserMap = todos.reduce((accumulator, todo)=>{
if (accumulator[todo.userId]) accumulator[todo.userId].push(todo);
if (!accumulator[todo.userId]) accumulator[todo.userId] = [todo];
return accumulator;
},{})
console.log(todosForUserMap)
})
这不会创建任何不必要的数组,而且更清晰,更容易理解。它与 forEach() 类似,但我建议使用它,因为它更清晰,更容易理解。
4. 使用生成器。
生成器和迭代器可能是 JavaScript 开发人员不常用的那些代码片段之一,它的知识仅限于编码面试。在数据获取的情况下,数据库/API 中的数据可能是无限的且量大,您将不得不在前端中流式传输它。在这种情况下,React 中最常用的解决方案是无限加载解决方案。
你会如何在 nodejs 服务器或原生 JavaScript 中实现类似无限加载的功能?
这就是迭代器非常有用的地方。我们可以使用异步生成器之类的方式,而不是在请求中流式传输数据到本地存储或其他地方,以便稍后检索。这是使用异步生成器的一种方法。通过这种方式,
我们可以在 JS 中解决类似无限加载的问题。
async function *fetchProducts(){
while (true){
const productUrl = "https://fakestoreapi.com/products?limit=2";
const res = await fetch(productUrl)
const data = await res.json()
yield data;
// 在这里操作 UI,比如
// 或将其保存在 DB 或其他地方
// 将其用作副作用的地方
// 即使某些条件匹配,也会中断流
}
}
async function main() {
const itr = fetchProducts();
// 根据用户交互或其他技巧调用此函数,因为您不希望无限加载。
console.log( await itr.next() );
}
return main()
5. 使用原生 JavaScript 类。
JavaScript 自带原生 JavaScript 类,可以帮助您轻松创建/实例化诸如 URL、Headers 等东西。我们可能曾经看到有人试图像这样查询 URL 中的查询参数。
async function 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,这是您可以在代码中实现的许多设计模式之一,它将复杂逻辑隐藏在一个单独的地方,并提高了可读性。