在 javascript 里,如果我们想用一个函数处理数组 (Array) 中的每个元素,那我们有很多种选择,最简单的当然就是用自带的 forEach 函数(低版本也可以使用 lodash 中的 forEach 函数):

const arr = [0,1,2,3,4,5,6,7,8,9];

arr.forEach(item=>{ console.log(item) });//依次输出

除了这种遍历,数组还有一种很常用的操作,就是拿来递归,js中的数组自带了 pop 和 push 方法,其实也可以当作一个链表来用,配合递归自然也是相当好用:

const arr = [0,1,2,3,4,5,6,7,8,9];

const func = (arr)=>{
item = arr.pop();
console.log(item);
if (arr.length==0) return;
return func(arr);
}
func(arr)

这样也能实现和之前 forEach 类似的效果~

既然效果差不多,那我们为啥要搞出这么麻烦的东西??

嘛……有些场景下遍历操作也不是那么好用的啦……比如我以前博文中写到的那个爬虫

"use strict"
const request = require('request')
const fs = require('fs') const arr = [
'http://path.to/img1.jpg',
'http://path.to/img2.jpg',
'http://path.to/img3.jpg',
...
'http://path.to/img498.jpg',
'http://path.to/img499.jpg',
'http://path.to/img500.jpg'
] arr.forEach(src=> {
//下载图片
const ws = fs.createWriteStream('./download/'+src.split('/').pop());
ws.on('close', ()=>{
console.log('下载完成')
})
request(src).pipe(ws);
})

因为 request 是一个异步操作,所以它并不会阻塞 forEach ,也就是说上面这个 demo 一旦运行起来,就会创建500个并发的网络请求和文件写入……

这就不太好玩了,一般来说并发请求可以提高网络利用效率,但效率再怎么提高,带宽也是有限的,并发过多就会导致单个请求变慢,请求过慢就可能会被服务端干掉,被服务端干掉的话我们就拿不到想要的图片了



所以我们期望的效果是依次执行下载操作,下载完一个再去下载另一个,这时候就比较适合用递归了~

"use strict"
const request = require('request')
const fs = require('fs') const arr = [
'http://path.to/img1.jpg',
'http://path.to/img2.jpg',
'http://path.to/img3.jpg',
...
'http://path.to/img498.jpg',
'http://path.to/img499.jpg',
'http://path.to/img500.jpg'
] const download = (arr)=>{
src = arr.pop();
//下载图片
const ws = fs.createWriteStream('./download/'+src.split('/').pop());
ws.on('close', ()=>{
console.log('下载完成')
if (arr.length>0)
return download(arr);
})
request(src).pipe(ws);
}
download (arr);

这样我们就可以依次下载图片啦~

可是既然是经常会用的东西……有没有更方便的用法啊,就像forEach那样的……?

那把递归这个操作抽象出来,直接定义到数组的原型 (prototype) 上吧

Array.prototype.recursion = function (func) {
const re = function (arr) {
if (!arr.length) return;
const item = arr.pop();
return func(item, function () {
return re(arr);
});
}
re(this);
}

于是乎这样随便写了一下,虽然很简陋,但好歹是可以用的, 使用方式如下:

"use strict"
const request = require('request')
const fs = require('fs') const arr = [
'http://path.to/img1.jpg',
'http://path.to/img2.jpg',
'http://path.to/img3.jpg',
...
'http://path.to/img498.jpg',
'http://path.to/img499.jpg',
'http://path.to/img500.jpg'
] arr.recursion((src, next)=> {
//下载图片
const ws = fs.createWriteStream('./download/'+src.split('/').pop());
ws.on('close', ()=>{
console.log('下载完成');
//当前异步操作完成后,调用next来进行下一次操作
next()
})
request(src).pipe(ws);
})

其实我也不知道这样做是否合适,链表加递归这种做法毕竟还是更适合函数式风格,而操作原型这种做法更多的则是面向对象风格,函数式编程比较强调无副作用,而显然……我现在这种做法是有副作用的,递归过程中不断pop(),递归完成后,arr 就变成一个空数组了。

其实这也还只是一个半成品,我们可能还希望在递归完成的时候,再继续执行一些操作,比如说把下载下来的图片打个压缩包?或者发一条消息告诉我文件都下载完成了之类的。

不管怎么说,这也是一个思路,如果发现这个思路中有其他严重的问题,或者有更好的建议,也欢迎指教~

用递归的方式处理数组 && 把递归方法方法定义到数组的原型上 (这是一次脑洞大开的神奇尝试)的更多相关文章

  1. 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法

    具体见第三阶段scala-day01中的文档(scala编程基础---基础语法)  1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...

  2. php中对象转数组有哪些方法(总结测试)

    php中对象转数组有哪些方法(总结测试) 一.总结 一句话总结:json_decode(json_encode($array),true)和array强制转换(或带递归) 1.array方式强制转换对 ...

  3. 【前端基础系列】slice方法将类数组转换数组实现原理

    问题描述 在日常编码中会遇到将类数组对象转换为数组的问题,其中常用到的一种方式使用Array.prototype.slice()方法. 类数组对象 所谓的类数组对象,JavaScript对它们定义为: ...

  4. java基础:方法的定义和调用详细介绍,方法同时获取数组最大值和最小值,比较两个数组,数组交换最大最小值,附练习案列

    1. 方法概述 1.1 方法的概念 方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集 注意: 方法必须先创建才可以使用,该过程成为方法定义 方法创建后并不是直接可 ...

  5. javascript中数组的concat()方法 - 数组连接

    <html> <head> <title>数组的concat()方法</title> <script> /* 数组的concat()方法: ...

  6. php中获得数组长度的方法

    php中获得数组长度的方法   count统计数组里元素的个数:  strlen是统计数组中元素的长度: 你如果想统计数组中所有元素的长度,那就用循环统计吧tqeb 代码: $a  =  array( ...

  7. 【前端_js】javascript中数组的map()方法

    数组的map()方法用于遍历数组,每遍历一个元素就调用回调方法一次,并将回调函数的返回结果作为新数组的元素,被遍历的数组不会被改变. 语法:let newAarray = arr.map(functi ...

  8. php用压栈的方式,循环遍历无限级别的数组(非递归方法)

    php用压栈的方式,循环遍历无限级别的数组(非递归方法) 好久不写非递归遍历无限级分类...瞎猫碰到死老鼠,发刚才写的1段代码,压栈的方式遍历php无限分类的数组... php压栈的方式遍历无限级别数 ...

  9. js数组去重 数组拼接 替换数组中的指定值 递归数组 判断数组中是否存在指定值 数组求和 根据条件判数组值

    这是学习过程中记录的一些关于数组操作的常用属性或方法,记录一下方便以后使用. // 数组去重 var arr1 = [1,1,2,3,4,5,6,3,2,4,5,'a','b','c','a',6,7 ...

随机推荐

  1. POJ1556(割点)

    SPF Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 8114   Accepted: 3716 Description C ...

  2. HDU3410(单调队列)

    Passing the Message Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Othe ...

  3. Java各种工具下载

    http://yunpan.cn/cyUzqFTu8pEER  提取码 355c  myeclipse2013 http://yunpan.cn/cyUzPi7nC8Z9S  提取码 fc4b  my ...

  4. 深圳尚学堂:JavaScript的常见面试题

    经常说编程是一门技术专业,在找工作时面试官肯定不止看你的面试能力,还要看你的专业知识掌握,他可能会让你做一个小的编程测试,或者说考察你的语法知识掌握,今天,总结了一些关于在JavaScript中常常会 ...

  5. [html5] 学习笔记-响应式布局

    1.响应式布局介绍 响应式布局是2010年5月份提出的一个概念,简而言之,就是一个网站能够兼容多个终端——而不是每一个终端做一个特定的版本.这个概念是为了兼容移动互联网浏览而诞生的,其目的是为用户提供 ...

  6. Codeforces 708A Letters Cyclic Shift

    A. Letters Cyclic Shift time limit per test:1 second memory limit per test:256 megabytes input:stand ...

  7. vs基础:无法断点调试dll项目 无法命中

    调试vs时,经常会出现,你设置了dll项目的一些断点,可f5之后,这些断点无效.时代定制的程序组的童鞋告诉你解决方法:在解决方案上右键“属性”,点击左侧树“配置属性”-->“配置”,右侧项目列表 ...

  8. Python学习--20 Web开发

    HTTP格式 HTTP协议是基于TCP和IP协议的.HTTP协议是一种文本协议. 每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的. HTTP ...

  9. 用js实现文字提示层 ---总结

    文字提示层在项目中应该是比较常见的,我工作中项目中就用到了,原理是一样,只不过形式不一样,今天通过看视频学习,学会了,总结下: 首先,页面效果如下:  需求: 当鼠标移入到红色字体是,提示框会显示在下 ...

  10. NHibernate的常见问题及解决方案

    问题1 : 异常:in expected: <end-of-text> (possibly an invalid or unmapped class name was used in th ...