使用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 formValidator表单验证插件开源了 含API帮助、源码、示例
Aug 14 Javascript
TreeView 用法(有代码)(asp.net)
Jul 15 Javascript
jquery怎样实现ajax联动框(二)
Mar 08 Javascript
jquery用get实现ajax在ie里面刷新不进入后台解决方法
Aug 12 Javascript
常见表单重复提交问题整理及解决方法
Nov 13 Javascript
JS是按值传递还是按引用传递
Jan 30 Javascript
鼠标悬停小图标显示大图标
Jan 22 Javascript
JavaScript获取IP获取的是IPV6 如何校验
Jun 12 Javascript
原生javascript实现的全屏滚动功能示例
Sep 19 Javascript
JavaScript原型式继承实现方法
Nov 06 Javascript
js实现点赞按钮功能的实例代码
Mar 06 Javascript
5个实用的JavaScript新特性
Jun 16 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开发工具之vs2005图解
2008/01/12 PHP
WordPress中制作导航菜单的PHP核心方法讲解
2015/12/11 PHP
PHP使用Redis长连接的方法详解
2018/02/12 PHP
模拟select的代码
2011/10/19 Javascript
qTip2 精致的基于jQuery提示信息插件
2012/02/17 Javascript
javascript自然分类法算法实现代码
2013/10/11 Javascript
jquery隐藏标签和显示标签的实例
2013/11/11 Javascript
微信分享的标题、缩略图、连接及描述设置方法
2014/10/14 Javascript
JavaScript中的ArrayBuffer详细介绍
2014/12/08 Javascript
jQuery对象初始化的传参方式
2015/02/26 Javascript
js对象浅拷贝和深拷贝详解
2016/09/05 Javascript
Bootstrap + AngularJS 实现简单的数据过滤字符查找功能
2017/07/27 Javascript
p5.js入门教程之小球动画示例代码
2018/03/15 Javascript
详解微信小程序实现跑马灯效果(附完整代码)
2019/04/29 Javascript
详解基于Vue/React项目的移动端适配方案
2019/08/23 Javascript
利用JS如何获取form表单数据
2019/12/19 Javascript
微信小程序实现签字功能
2019/12/23 Javascript
Vue Router 实现动态路由和常见问题及解决方法
2020/03/06 Javascript
基于JavaScript实现控制下拉列表
2020/05/08 Javascript
[01:25:33]完美世界DOTA2联赛PWL S3 INK ICE vs Magma 第二场 12.20
2020/12/23 DOTA
Python+OpenCV图片局部区域像素值处理详解
2019/01/23 Python
python 比较2张图片的相似度的方法示例
2019/12/18 Python
玩具反斗城西班牙网上商城:ToysRUs西班牙
2017/01/19 全球购物
世界顶级俱乐部的官方球衣和套装:Subside Sports
2018/04/22 全球购物
美国婚礼礼品网站:MyWeddingFavors
2018/09/26 全球购物
英国领先的在线高尔夫设备零售商:Golfgeardirect
2020/12/11 全球购物
物流合作计划书
2014/01/10 职场文书
五星级酒店餐饮部总监的标准岗位职责
2014/02/17 职场文书
镇班子对照检查材料思想汇报
2014/09/24 职场文书
2014年预算员工作总结
2014/12/05 职场文书
西安大雁塔导游词
2015/02/10 职场文书
2016年大学自主招生自荐信范文
2015/03/24 职场文书
绿色环保倡议书
2015/04/28 职场文书
撤诉状格式范本
2015/05/19 职场文书
烈士陵园扫墓感想
2015/08/07 职场文书
个人的事迹材料怎么写
2019/04/24 职场文书