使用apply方法处理数组的三个技巧[译]


Posted in Javascript onSeptember 20, 2012

apply方法

apply是所有函数都有的方法.它的签名如下:
func.apply(thisValue, [arg1, arg2, ...])
如果不考虑thisValue的影响,上面的调用等同于:
func(arg1, arg2, ...)
也就是说,apply允许我们将一个数组"解开"成为一个个的参数再传递给调用函数.让我们分别看看apply使用中的三个技巧.

技巧1: 将一个数组传递给一个不接受数组作为参数的函数

JavaScript中没有返回一个数组中最大值的函数.但是,有一个函数Math.max可以返回任意多个数值类型的参数中的最大值.再配合apply,我们可以实现我们的目的:

> Math.max.apply(null, [10, -1, 5]) 
10

译者注:注意Math.max方法的参数中只要有一个值被转为NaN,则该方法直接返回NaN
>Math.max(1,null) //相当于Math.max(1,0) 
1 
>Math.max(1,undefinded) //相当于Math.max(1,NaN) 
NaN >Math.max(0,-0) //正零比负零大,和==不同 
0 
>Math.max(-0,-1) //负零比-1大 
-0

技巧2: 填补稀疏数组

数组中的缝隙
这里提醒一下读者:在JavaScript中,一个数组就是一个数字到值的映射.所以如果某个索引处缺失了一个元素(一条缝隙)和某个元素的值为undefined,是两种不同的情况.前者在被Array.prototype中的相关方法(forEach, map, 等.)遍历时,会跳过那些缺失的元素,而后者不会:

> ["a",,"b"].forEach(function (x) { console.log(x) }) 
a 
> ["a",undefined,"b"].forEach(function (x) { console.log(x) }) 
a 
undefined

译者注:这里作者说"数组就是一个数字到值的映射",严格意义上是不对的,正确的说法是"数组就是一个字符串到值的映射".下面是证据:
>for (i in ["a", "b"]) { 
console.log(typeof i) //数组的索引实际上是个字符串 
} 
"string" 
"string" >["a", "b"].forEach(function (x, i) { 
console.log(typeof i) //这里的i实际上不是索引,只是个数字类型的累加器 
}) 
"number" 
"number"

你可以使用in运算符来检测数组中是否有缝隙.
> 1 in ["a",,"b"] 
false 
> 1 in ["a", undefined, "b"] 
true

译者注:这里之所以用1可以,是因为in运算符会把1转换成"1".

你过你尝试读取这个缝隙的值,会返回undefined,和实际的undefined元素是一样.

> ["a",,"b"][1] 
undefined 
> ["a", undefined, "b"][1] 
undefined

译者注:[1]也会被转换成["1"]

填补缝隙

apply配合Array(这里不需要加new)使用,可以将数组中的缝隙填补为undefined元素:

> Array.apply(null, ["a",,"b"]) 
[ 'a', undefined, 'b' ]

这都是因为apply不会忽略数组中的缝隙,会把缝隙作为undefined参数传递给函数:

> function returnArgs() { return [].slice.call(arguments) } 
> returnArgs.apply(null, ["a",,"b"]) 
[ 'a', undefined, 'b' ]

但需要注意的是,如果Array方法接收到的参数是一个单独的数字,则会把这个参数当成数组长度,返回一个新数组:
> Array.apply(null, [ 3 ]) 
[ , , ]

因此,最靠谱的方法是写一个这样的函数来做这种工作:
function fillHoles(arr) { 
var result = []; 
for(var i=0; i < arr.length; i++) { 
result[i] = arr[i]; 
} 
return result; 
}

执行:
> fillHoles(["a",,"b"]) 
[ 'a', undefined, 'b' ]

Underscore中的_.compact函数会移除数组中的所有假值,包括缝隙:

> _.compact(["a",,"b"]) 
[ 'a', 'b' ] 
> _.compact(["a", undefined, "b"]) 
[ 'a', 'b' ] 
> _.compact(["a", false, "b"]) 
[ 'a', 'b' ]

技巧3: 扁平化数组

任务:将一个包含多个数组元素的数组转换为一个一阶数组.我们利用apply解包数组的能力配合concat来做这件事:

> Array.prototype.concat.apply([], [["a"], ["b"]]) 
[ 'a', 'b' ]

混合非数组类型的元素也可以:
> Array.prototype.concat.apply([], [["a"], "b"]) 
[ 'a', 'b' ]

apply方法的thisValue必须指定为[],因为concat是一个数组的方法,不是一个独立的函数.这种写法的限制是最多只能扁平化二阶数组:
> Array.prototype.concat.apply([], [[["a"]], ["b"]]) 
[ [ 'a' ], 'b' ]

所以你应该考虑一个替代方案.比如Underscore中的_.flatten函数就可以处理任意层数的嵌套数组

> _.flatten([[["a"]], ["b"]]) 
[ 'a', 'b' ]

参考
JavaScript: sparse arrays vs. dense arrays

ECMAScript.next: Array.from() and Array.of()

Javascript 相关文章推荐
十个优秀的Ajax/Javascript实例网站收集
Mar 31 Javascript
JavaScript游戏之优化篇
Nov 08 Javascript
jQuery ready函数滥用分析
Feb 16 Javascript
js中对象的声明方式以及数组的一些用法示例
Dec 11 Javascript
Jquery实现弹性滑块滑动选择数值插件
Aug 08 Javascript
jQuery获取DOM节点实例分析(2种方式)
Dec 15 Javascript
jQuery焦点图轮播插件KinSlideshow用法分析
Jun 08 Javascript
JS实现拖拽的方法分析
Dec 20 Javascript
vue.js实例todoList项目
Jul 07 Javascript
详解Node 定时器
Feb 26 Javascript
JS实现用特殊符号替换字符串的中间部分区域的实例代码
Jul 24 Javascript
为什么推荐使用JSX开发Vue3
Dec 28 Vue.js
js DOM 元素ID就是全局变量
Sep 20 #Javascript
JavaScript NaN和Infinity特殊值 [译]
Sep 20 #Javascript
JavaScript 更严格的相等 [译]
Sep 20 #Javascript
JavaScript 反科里化 this [译]
Sep 20 #Javascript
Array.prototype.concat不是通用方法反驳[译]
Sep 20 #Javascript
JavaScript 用Node.js写Shell脚本[译]
Sep 20 #Javascript
一个简单的网站访问JS计数器 刷新1次加1次访问
Sep 20 #Javascript
You might like
杏林同学录(一)
2006/10/09 PHP
require(),include(),require_once()和include_once()的异同
2007/01/02 PHP
解析CodeIgniter自定义配置文件
2013/06/18 PHP
PHP连接Access数据库的方法小结
2013/06/20 PHP
PHP开发实现微信退款功能示例
2017/11/25 PHP
Windows上php5.6操作mongodb数据库示例【配置、连接、获取实例】
2019/02/13 PHP
TNC vs BOOM BO3 第二场2.13
2021/03/10 DOTA
关于火狐(firefox)及ie下event获取的两种方法
2012/12/27 Javascript
Extjs Gird 支持中文拼音排序实现代码
2013/04/15 Javascript
再谈Jquery Ajax方法传递到action(补充)
2014/05/12 Javascript
Javascript核心读书有感之表达式和运算符
2015/02/11 Javascript
JS获取随机数和时间转换的简单实例
2016/07/10 Javascript
基于MVC5和Bootstrap的jQuery TreeView树形控件(一)之数据支持json字符串、list集合
2016/08/11 Javascript
vue多级多选菜单组件开发
2020/09/08 Javascript
bootstrap treeview 扩展addNode方法动态添加子节点的方法
2017/11/21 Javascript
微信小程序实现图片上传功能实例(前端+PHP后端)
2018/01/10 Javascript
微信小程序中使用ECharts 异步加载数据的方法
2018/06/27 Javascript
Vue中使用的EventBus有生命周期
2018/07/12 Javascript
Vue路由history模式解决404问题的几种方法
2018/09/29 Javascript
浅谈Vuex注入Vue生命周期的过程
2019/05/20 Javascript
python简单程序读取串口信息的方法
2015/03/13 Python
Python中利用sqrt()方法进行平方根计算的教程
2015/05/15 Python
python分数表示方式和写法
2019/06/26 Python
python中plt.imshow与cv2.imshow显示颜色问题
2020/07/16 Python
Python Tricks 使用 pywinrm 远程控制 Windows 主机的方法
2020/07/21 Python
HTML5是否真的可以取代Flash
2010/02/10 HTML / CSS
HTML5无刷新改变当前url的代码
2017/03/15 HTML / CSS
英国时尚饰品和发饰购物网站:Claire’s
2017/07/04 全球购物
美国眼镜网:GlassesUSA
2017/09/07 全球购物
党的生日演讲稿
2014/09/10 职场文书
《鲁班学艺》读后感3篇
2019/11/27 职场文书
MySQL root密码的重置方法
2021/04/21 MySQL
python opencv通过4坐标剪裁图片
2021/06/05 Python
anaconda python3.8安装后降级
2021/06/11 Python
利用Python多线程实现图片下载器
2022/03/25 Python
SpringBoot项目多数据源及mybatis 驼峰失效的问题解决方法
2022/07/07 Java/Android