使用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 相关文章推荐
JavaScript的面向对象(一)
Nov 09 Javascript
超酷的网页音乐播放器DewPlayer使用方法
Dec 18 Javascript
关于锚点跳转及jQuery下相关操作与插件
Oct 01 Javascript
JavaScript实现大数的运算
Nov 24 Javascript
原生JS和JQuery动态添加、删除表格行的方法
May 28 Javascript
基于Jquery实现焦点图淡出淡入效果
Nov 30 Javascript
jQuery实现的超链接提示效果示例【附demo源码下载】
Sep 09 Javascript
jQuery绑定事件的四种方式介绍
Oct 31 Javascript
浅谈js中几种实用的跨域方法原理详解
Dec 02 Javascript
Ajax 加载数据 练习代码
Jan 05 Javascript
深入理解node.js http模块
Jan 24 Javascript
用原生 JS 实现 innerHTML 功能实例详解
Apr 03 Javascript
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
php实现上传图片文件代码
2015/07/19 PHP
PHP6连接SQLServer2005的三部曲
2016/04/15 PHP
php处理json格式数据经典案例总结
2016/05/19 PHP
Yii2语言国际化自动配置详解
2018/08/22 PHP
JavaScript TO HTML 转换
2006/06/26 Javascript
js制作的鼠标悬浮时产生的下拉框效果
2012/10/27 Javascript
js对象与打印对象分析比较
2013/04/23 Javascript
JS实现带缓冲效果打开、关闭、移动一个层的方法
2015/05/09 Javascript
通过实例理解javascript中没有函数重载的概念
2015/06/03 Javascript
Javascript实现网络监测的方法
2015/07/31 Javascript
js字符串引用的两种方式(必看)
2016/09/18 Javascript
Bootstrap源码学习笔记之bootstrap进度条
2016/12/24 Javascript
jQuery Validation Engine验证控件调用外部函数验证的方法
2017/01/18 Javascript
javascript 中的继承实例详解
2017/05/05 Javascript
js 简易版滚动条实例(适用于移动端H5开发)
2017/06/26 Javascript
深入理解JavaScript和TypeScript中的class
2018/04/22 Javascript
JS实现点击拉拽轮播图pc端移动端适配
2018/09/05 Javascript
JavaScript基础教程之如何实现一个简单的promise
2018/09/11 Javascript
详解Node.js amqplib 连接 Rabbit MQ最佳实践
2019/01/24 Javascript
JavaScript实现烟花绽放动画效果
2020/08/04 Javascript
vue组件开发之tab切换组件使用详解
2020/08/21 Javascript
Python 过滤字符串的技巧,map与itertools.imap
2008/09/06 Python
numpy中实现ndarray数组返回符合特定条件的索引方法
2018/04/17 Python
浅述python中深浅拷贝原理
2018/09/18 Python
python ipset管理 增删白名单的方法
2019/01/14 Python
pyqt 实现QlineEdit 输入密码显示成圆点的方法
2019/06/24 Python
win10下python2和python3共存问题解决方法
2019/12/23 Python
Python for循环通过序列索引迭代过程解析
2020/02/07 Python
解决python 虚拟环境删除包无法加载的问题
2020/07/13 Python
HTML5中使用json对象的实例代码
2018/09/10 HTML / CSS
洛杉矶健身中心女性专用运动服饰品牌:Marika
2018/05/09 全球购物
办公室保洁员岗位职责
2013/12/02 职场文书
ktv筹备计划书
2014/05/03 职场文书
考生诚信考试承诺书
2014/05/23 职场文书
银行优秀员工推荐信
2015/03/24 职场文书
关于vue-router-link选择样式设置
2022/04/30 Vue.js