使用reduce时,避免对accumulator执行副作用
const nextItems = items.reduce((acc, curr) => {
if (!acc.includes(curr)) {
// 副作用
acc.push(curr);
}
return acc;
}, []);
console.log('nextItems', nextItems);
去掉副作用
const items = ['张三', '李四', '张三'];
const nextItems = items.reduce((acc, curr) => {
// 没有副作用
return !acc.includes(curr) ? [...acc, curr] : [...acc];
}, []);
console.log('nextItems', nextItems);
优化对一份数据的多次遍历
const task = [
{ name: 'xx', finished: false },
{ name: 'xxx', finished: false }
];
const unfinishedTasks = tasks.filter(item => !item.finished);
const finishedTasks = tasks.filter(item => item.finished);
使用reduce
来改进:
const { unfinishedTasks, finishedTasks } = tasks.reduce(
(acc, task) => {
if (task.finished) {
acc.finishedTasks.push(task);
} else {
acc.unfinishedTasks.push(task);
}
return acc
},
{ unfinishedTasks: [], finishedTasks: [] }
);
去掉副作用代码:
const { unfinishedTasks, finishedTasks } = tasks.reduce(
(acc, task) => {
return task.finished
? { ...acc, finishedTasks: [...acc.finishedTasks, task] }
: { ...acc, unfinishedTasks: [...acc.unfinishedTasks, task] };
},
{ unfinishedTasks: [], finishedTasks: [] }
);
对find的结果进行判空
const items = [
{
id: 1,
name: 'Ben',
age: 25,
},
{
id: 2,
name: 'Lily',
age: 20,
},
];
const targetItem = items.find((item) => item.age === 50);
console.log(targetItem.name);
// Error: Cannot read properties of undefined (reading 'name')
上面的代码中,我们在find
的返回值上直接访问属性,会有可能导致异常。
因为在没有找到目标时,find
的返回值会是undefined
,在undefined
上访问对象,就会抛出异常。
所以,遵循简单的策略,永远对find
的返回值进行判空,避免意外情况。
const targetItem = items.find((item) => item.age === 50);
console.log(targetItem?.name);
// undefined
利用map与filter组合过滤
目标:
剔除users
不存在于permissions
中的用户数据,并且将permissions
的codes
写入users
。
// 用户列表数据
const users = [
{
id: 1,
name: 'Ben',
age: 25,
codes: [],
},
{
id: 2,
name: 'Lily',
age: 20,
codes: [],
},
];
// 用户的权限code数据
const permissions = [
{
id: 1,
codes: ['create', 'delete'],
},
{
id: 3,
codes: ['create', 'delete'],
},
];
先给出一个符合直觉的版本
方案1:
- 遍历
users
,检查每个user
是否存在于permissions
,得到hasPermissonUsers
- 对上面的
hasPermissonUsers
进行遍历,从permissions
中取出每个user
的codes
const adminUsers = users
.filter((user) => permissions.find((permission) => permission.id === user.id))
.map((user) => ({
...user,
codes: permissions.find((permission) => permission.id === user.id)?.codes ?? [],
}));
上面的代码很好的完成了任务,但是还有一点优化的空间。
在上面的代码中,filter
和map
内部都对permissions
进行了一次遍历,有没有办法避免呢?
方案2:
- 遍历
users
,获取每个user
对应的permission
,将获取到的permission.codes
存到当前user
中,将无匹配结果的数据作为null
返回 - 对
users
进行一次真假值检查,即可过滤掉不存在于permissions
的用户
const betterAdminUsers = users
.map((user) => {
// 用target来实现2个目的
const target = permissions.find((permission) => permission.id === user.id);
// 1 获取permission中的codes
if (target) {
return {
...user,
codes: target.codes,
};
}
// 2 用于给后面的filter过滤掉不存在于permissions的用户
return null;
})
.filter(Boolean);
方案2中,我们减少了一次find
查询,而且代码变得更简单了。
关键点就是,在map
内部,将本次对permissions
的查询结果通过某种方式传递给了filter
,避免了filter
内部再次permissions
对进行查询。
针对方案2的更新:
这种在map
里面干两件事的代码,违反了单一职责,感觉没有必要,增加了阅读者的心智负担。
这里对方案1进行了一点小改造,这样map
里面保证了单一职责,它只做了数据转存这一件事。然后filter
也只做了过滤这一件事。
const adminUsers = users
.map((user) => {
const target = permissions.find((permission) => permission.id === user.id);
return {
...user,
isHasPermission: !!target,
codes: target?.codes ?? [],
};
})
.filter((user) => user.isHasPermission);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。