使用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 相关文章推荐
JQuery实现的在新窗口打开链接的方法小结
Apr 22 Javascript
JavaScript中的关键字&quot;VAR&quot;使用详解 分享
Jul 31 Javascript
基于JS实现导航条之调用网页助手小精灵的方法
Jun 17 Javascript
详解Bootstrap的iCheck插件checkbox和radio
Aug 24 Javascript
fckeditor部署到weblogic出现xml无法读取及样式不能显示问题的解决方法
Mar 24 Javascript
Vue项目中如何引入icon图标
Mar 28 Javascript
vue实现自定义多选与单选的答题功能
Jul 05 Javascript
详解如何在webpack中做预渲染降低首屏空白时间
Aug 22 Javascript
如何手动实现es5中的bind方法详解
Dec 07 Javascript
javascriptvoid(0)含义以及与&quot;#&quot;的区别讲解
Jan 19 Javascript
jQuery中event.target和this的区别详解
Aug 13 jQuery
js前端设计模式优化50%表单校验代码示例
Jun 21 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
cmd下运行php脚本
2008/11/25 PHP
PHP持久连接mysql_pconnect()函数使用介绍
2012/02/05 PHP
PHPMailer发送HTML内容、带附件的邮件实例
2014/07/01 PHP
PHP简单判断手机设备的方法
2016/08/23 PHP
PHP实现对图片的反色处理功能【测试可用】
2018/02/01 PHP
php实现单笔转账到支付宝功能
2018/10/09 PHP
jQuery 获取URL参数的插件
2010/03/04 Javascript
js定义对象或数组直接量时各浏览器对多余逗号的处理(json)
2011/03/05 Javascript
js完美的div拖拽实例代码
2014/01/22 Javascript
jQuery使用ajaxSubmit()提交表单示例
2014/04/04 Javascript
javascript变量声明实例分析
2015/04/25 Javascript
基于JS快速实现导航下拉菜单动画效果附源码下载
2016/10/27 Javascript
vue双向数据绑定原理探究(附demo)
2017/01/17 Javascript
JS实现动态添加DOM节点和事件的方法示例
2017/04/28 Javascript
JS常见DOM节点操作示例【创建 ,插入,删除,复制,查找】
2018/05/14 Javascript
MVVM框架下实现分页功能示例
2018/06/14 Javascript
Vue对象赋值视图不更新问题及解决方法
2019/06/03 Javascript
微信小程序iOS下拉白屏晃动问题解决方案
2019/10/12 Javascript
js实现滑动滑块验证登录
2020/07/24 Javascript
[03:02]生活中的Dendi之野外度假篇
2016/08/09 DOTA
python日志记录模块实例及改进
2017/02/12 Python
Sanic框架Cookies操作示例
2018/07/17 Python
树莓派实现移动拍照
2019/06/22 Python
解决Pycharm 导入其他文件夹源码的2种方法
2020/02/12 Python
python2 对excel表格操作完整示例
2020/02/23 Python
Python使用Selenium实现淘宝抢单的流程分析
2020/06/23 Python
Python3爬虫中Selenium的用法详解
2020/07/10 Python
Python unittest如何生成HTMLTestRunner模块
2020/09/08 Python
找到不普通的东西:Bonanza
2016/10/20 全球购物
M.M.LaFleur官网:美国职业女装品牌
2020/10/27 全球购物
Java文件和目录(IO)操作
2014/08/26 面试题
服装创业计划书范文
2014/02/05 职场文书
乡镇精神文明建设汇报材料
2014/08/15 职场文书
税务干部群众路线教育实践活动对照检查材料
2014/09/20 职场文书
五年级数学教学反思
2016/02/16 职场文书
git stash(储藏)的用法总结
2022/06/25 Servers