使用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 相关文章推荐
动态加载js文件 document.createElement
Oct 14 Javascript
JQuery中dataGrid设置行的高度示例代码
Jan 03 Javascript
用json方式实现在 js 中建立一个map
May 02 Javascript
Node.js 异步编程之 Callback介绍(一)
Mar 30 Javascript
jquery 实现滚动条下拉时无限加载的简单实例
Jun 01 Javascript
BootStrap树状图显示功能
Nov 24 Javascript
实例浅析js的this
Dec 11 Javascript
三分钟学会用ES7中的Async/Await进行异步编程
Jun 14 Javascript
React中使用async validator进行表单验证的实例代码
Aug 17 Javascript
webpack4简单入门实例
Sep 06 Javascript
微信 jssdk 签名错误invalid signature的解决方法
Jan 14 Javascript
jQuery 添加元素和删除元素的方法
Jul 15 jQuery
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
再次研究下cache_lite
2007/02/14 PHP
php使用escapeshellarg时中文被过滤的解决方法
2016/07/10 PHP
让插入到 innerHTML 中的 script 跑起来的实现代码
2006/07/01 Javascript
javascript代码编写需要注意的7个小细节小结
2011/09/21 Javascript
JS数组的常见用法实例
2015/02/10 Javascript
JavaScript模拟实现继承的方法
2015/03/30 Javascript
Javascript实现快速排序(Quicksort)的算法详解
2015/09/06 Javascript
基于JavaScript实现 网页切出 网站title变化代码
2016/04/03 Javascript
JQuery和PHP结合实现动态进度条上传显示
2016/11/23 Javascript
微信小程序 数据交互与渲染实例详解
2017/01/21 Javascript
ie下js不执行的几种可能
2017/02/28 Javascript
详解Angular.js中$http拦截器的介绍及使用
2017/07/04 Javascript
javascript代码优化的8点总结
2018/01/29 Javascript
更强大的vue ssr实现预取数据的方式
2019/07/19 Javascript
详解vue中使用transition和animation的实例代码
2020/12/12 Vue.js
Python 类的特殊成员解析
2018/06/20 Python
使用python制作一个为hex文件增加版本号的脚本实例
2019/06/12 Python
Python求均值,方差,标准差的实例
2019/06/29 Python
python使用百度文字识别功能方法详解
2019/07/23 Python
Python 离线工作环境搭建的方法步骤
2019/07/29 Python
python matplotlib库绘制散点图例题解析
2019/08/10 Python
Python基于Hypothesis测试库生成测试数据
2020/04/29 Python
python virtualenv虚拟环境配置与使用教程详解
2020/07/13 Python
python中的对数log函数表示及用法
2020/12/09 Python
html5指南-6.如何创建离线web应用程序实现离线访问
2013/01/07 HTML / CSS
精彩的大学生自我评价
2013/11/17 职场文书
大学生怎样写好自荐信
2014/02/25 职场文书
法定代表人身份证明书
2014/09/10 职场文书
高校群众路线教育实践活动剖析材料
2014/10/10 职场文书
违反交通安全法检讨书
2014/10/24 职场文书
家属答谢词
2015/01/05 职场文书
简历自荐信范文
2015/03/09 职场文书
2015年学生会纪检部工作总结
2015/03/31 职场文书
Redisson实现Redis分布式锁的几种方式
2021/08/07 Redis
CSS三大特性继承性、层叠性和优先级详解
2022/01/18 HTML / CSS
《废话连篇——致新手》——chinapizza
2022/04/05 无线电