之前接触的项目里面就产生过这样的疑问,但是当时做信息过滤的时候使用map和some写下的代码和使用for的差别实在过于明显(后者写了十几行,但是前者直接一行搞定),自此本以为map和for只是互有优劣,选用哪一个只是场景的差别问题。
但是后来开始实习,实习中接触到的场景更多,可是有意思的是这些场景下似乎使用map这类方法都要比for的编码效果更好。
于是现在这个疑问又跳了出来:难道说使用map这类数组自带的方法是可以完全替代for吗,或者说选用map还是for的标准究竟是什么?
for与Array.prototype.map()等方法的使用场景疑问
之前过面经的时候简单的看了一眼: forEach 中用 await 会产生什么问题
简单地说,对于异步代码,forEach 不能按照顺序执行,但是 for of 可以 上面这篇文章写得挺好的,建议读一读
根据我自己这方面的经验,这里主要是函数式编程的写法在 JS Array 的一点应用。大概是关注点和心智负担的差异,当程序员只想关心两个列表之间的数据映射和转化,不想纠结细节的时候,用 map 会更方便一些;相同的流程用 for 循环涉及到的改动什么的有可能会比较复杂。
大致的模式就是 List -- map 的回调 --> 新的List
。这在 React 里面 render 列表场景就用的特别多,比如说 这个例子,把一个 number 为元素 的 List 转化为 React Node 为元素 的 List:
function NumberList(props) {
const numbers = props.numbers;
const listItems = numbers.map((number) =>
<li>{number}</li>
);
return (
<ul>{listItems}</ul>
);
}
const numbers = [1, 2, 3, 4, 5];
ReactDOM.render(
<NumberList numbers={numbers} />,
document.getElementById('root')
);
for 循环大概就是一个命令式的实现,关注点是程序具体的执行流程,如果想写一些比较复杂的算法,涉及到有先后顺序的异步流程,还有精确使用循环啥的场景,都可能 for 就更适合一些。
- 已编辑
感觉二楼这里提到的异步问题,可能不用太纠结说 forEach 还是 for of 等细节写法有啥特点(用 for 来 await 估计也 ok),实际上都是正常顺序执行的。
这里异步 forEach 这种的问题主要是 JavaScript 的运行时的执行机制决定,JS 引擎主要通过单线程事件循环的方式来持续运转。我们无论用 for 还是 forEach 还是 map 啥的,当执行到一个涉及到异步操作的语句的时候,JS 引擎相当于往某个异步线程派发了一个新的任务,注册了一个回调函数,然后又继续执行下一条语句,不会等待异步操作的完成。
异步任务完成触发回调之后,引擎会把对应的回调函数的执行任务加入任务队列,然后事件循环在消费到这个队列的时候才会执行这里的回调函数;forEach 调用相当于只是按顺序启动了任务,但无法控制任务何时完成,乃至于一系列任务的回调被加入执行队列的先后顺序。
然后社区为了更好地方便开发者去控制任务的顺序,就提出了 Promise 对象的方式去做辅助,Promise 相当于代理了异步任务的回调,然后统一用 (Promise Object).then(callback)
的方式去组织这里的任务。但 Promise 一路 then 下去还是不很优雅,再进一步就发展出了 async await 这样的函数去辅助包装 Promise 的异步逻辑(类似于在语言层面把 await 后面的代码都组织到 then 的回调里面了),产生的好处是它写起来和一般的命令式写法相似,还可以很灵活地控制它们的顺序。
把握了这一点以后甚至还可以结合 map 写一些并发的异步请求逻辑(最近我自己也经常这么干~
类似这样的伪代码:
(async () => {
const imgUrlList = ['https://baidu.com/1.jpg', 'https://baidu.com/2.jpg', 'https://baidu.com/3.jpg'];
const fetchPromiseList = imgUrlList.map(async (url) => await fetch(url).blob());
const imgBlobList = await Promise.all(fetchPromiseList);
imgBlobList.forEach(imgBlob => download(imgBlob));
// ...
})();