用递归的方式处理数组 && 把递归方法方法定义到数组的原型上 (这是一次脑洞大开的神奇尝试)
在 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 就变成一个空数组了。
其实这也还只是一个半成品,我们可能还希望在递归完成的时候,再继续执行一些操作,比如说把下载下来的图片打个压缩包?或者发一条消息告诉我文件都下载完成了之类的。
不管怎么说,这也是一个思路,如果发现这个思路中有其他严重的问题,或者有更好的建议,也欢迎指教~
用递归的方式处理数组 && 把递归方法方法定义到数组的原型上 (这是一次脑洞大开的神奇尝试)的更多相关文章
- 大数据学习day13------第三阶段----scala01-----函数式编程。scala以及IDEA的安装,变量的定义,条件表达式,for循环(守卫模式,推导式,可变参数以及三种遍历方式),方法定义,数组以及集合(可变和非可变),数组中常用的方法
具体见第三阶段scala-day01中的文档(scala编程基础---基础语法) 1. 函数式编程(https://www.cnblogs.com/wchukai/p/5651185.html): ...
- php中对象转数组有哪些方法(总结测试)
php中对象转数组有哪些方法(总结测试) 一.总结 一句话总结:json_decode(json_encode($array),true)和array强制转换(或带递归) 1.array方式强制转换对 ...
- 【前端基础系列】slice方法将类数组转换数组实现原理
问题描述 在日常编码中会遇到将类数组对象转换为数组的问题,其中常用到的一种方式使用Array.prototype.slice()方法. 类数组对象 所谓的类数组对象,JavaScript对它们定义为: ...
- java基础:方法的定义和调用详细介绍,方法同时获取数组最大值和最小值,比较两个数组,数组交换最大最小值,附练习案列
1. 方法概述 1.1 方法的概念 方法(method)是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集 注意: 方法必须先创建才可以使用,该过程成为方法定义 方法创建后并不是直接可 ...
- javascript中数组的concat()方法 - 数组连接
<html> <head> <title>数组的concat()方法</title> <script> /* 数组的concat()方法: ...
- php中获得数组长度的方法
php中获得数组长度的方法 count统计数组里元素的个数: strlen是统计数组中元素的长度: 你如果想统计数组中所有元素的长度,那就用循环统计吧tqeb 代码: $a = array( ...
- 【前端_js】javascript中数组的map()方法
数组的map()方法用于遍历数组,每遍历一个元素就调用回调方法一次,并将回调函数的返回结果作为新数组的元素,被遍历的数组不会被改变. 语法:let newAarray = arr.map(functi ...
- php用压栈的方式,循环遍历无限级别的数组(非递归方法)
php用压栈的方式,循环遍历无限级别的数组(非递归方法) 好久不写非递归遍历无限级分类...瞎猫碰到死老鼠,发刚才写的1段代码,压栈的方式遍历php无限分类的数组... php压栈的方式遍历无限级别数 ...
- js数组去重 数组拼接 替换数组中的指定值 递归数组 判断数组中是否存在指定值 数组求和 根据条件判数组值
这是学习过程中记录的一些关于数组操作的常用属性或方法,记录一下方便以后使用. // 数组去重 var arr1 = [1,1,2,3,4,5,6,3,2,4,5,'a','b','c','a',6,7 ...
随机推荐
- 游戏音频技术备忘 (四) Wwise Unreal Engine 集成代码浅析 (一)
在Engine\Plugins\Wwise\Source下为主要Wwise相关代码,AkAudio文件夹下为运行时相关代码,AudiokineticTools下为编辑器工具相关代码,Audiokine ...
- MongoDB基础之七 用户管理
MongoDB的用户管理 注意:A)在mongodb中,有一个admin数据库, 牵涉到服务器配置层面的操作,需要先切换到admin数据.即 use admin , -->相当于进入超级用户管理 ...
- Myeclipese改变背景色
https://www.baidu.com/s?wd=Myeclipese%E6%94%B9%E5%8F%98%E8%83%8C%E6%99%AF%E8%89%B2&ie=utf-8& ...
- MySQL性能优化方案
$stmt->execute(); // 绑定结果 $stmt->bind_result($username); // 移动游标 $stmt->fetch(); printf(&qu ...
- Git合并分支命令:git merge --ff
今天研究了一下git merge命令常用参数,并分别用简单的例子实验了一下,整理如下: 输入git merge -h可以查看相关参数: --ff 快速合并,这个是默认的参数.如果合并过程出现冲突,G ...
- 深入浅出ThreadLocal
前言 ThreadLocal为变量在每个线程中都创建了一个副本,所以每个线程可以访问自己内部的副本变量,不同线程之间不会互相干扰.本文会基于实际场景介绍ThreadLocal如何使用以及内部实现机制. ...
- DBMS 的个人理解
再看自己数据库时,感觉还是有点迷茫:数据库到底是怎们工作的呢?虽然之前把代码都弄了一遍,可是效果还是不太理想. 数据库到底是怎么连到用户的程序上的呢?它的内部到底是怎么运行的呢?我研究了一段时间,获得 ...
- 用css改变console.log的输出样式
console.log允许你通过css来格式化输出,格式如下: console.log('%c字符串[%c字符串]', 样式1, [样式2]) 其中"%c"为模板字符串 例子: 1 ...
- java字符串比较及小数浮点型的使用
import java.text.DecimalFormat; /* * 小数类型的常量默认是double类型,声明float类型的常量需要使用F作为后缀. * * 关于equals()和==: 对于 ...
- 改变radio默认样式
改变radio默认样式,代码如下: <!doctype html> <html> <head> <meta charset="UTF-8" ...