使用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 相关文章推荐
一个多次搜索+多次传值的解决方案
Jan 20 Javascript
javascript eval函数深入认识
Feb 21 Javascript
JQuery的ajax基础上的超强GridView展示
Sep 18 Javascript
node.js中的fs.fchmodSync方法使用说明
Dec 16 Javascript
jQuery给元素添加样式的方法详解
Dec 30 Javascript
详谈javascript异步编程
Feb 21 Javascript
基于JS实现省市联动效果代码分享
Jun 06 Javascript
RGB和YUV 多媒体编程基础详细介绍
Nov 04 Javascript
详解VUE里子组件如何获取父组件动态变化的值
Dec 26 Javascript
微信小程序+腾讯地图开发实现路径规划绘制
May 22 Javascript
vue学习笔记之slot插槽基本用法实例分析
Feb 01 Javascript
vuex的使用和简易实现
Jan 07 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
生成静态页面的PHP类
2006/07/15 PHP
php中限制ip段访问、禁止ip提交表单的代码分享
2014/08/22 PHP
Ubuntu中启用php的mail()函数并解决发送邮件速度慢问题
2015/03/27 PHP
PHP设置头信息及取得返回头信息的方法
2016/01/25 PHP
Thinkphp自定义代码生成工具及用法说明(附下载地址)
2016/05/27 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
PHP时间函数使用详解
2019/03/21 PHP
PHP面向对象程序设计之构造方法和析构方法详解
2019/06/13 PHP
Laravel 之url参数,获取路由参数的例子
2019/10/21 PHP
php传值和传引用的区别点总结
2019/11/19 PHP
jquery简单体验
2007/01/10 Javascript
jQuery ajax serialize() 方法使用示例
2014/11/02 Javascript
JavaScript学习笔记之定时器
2015/01/22 Javascript
分享两个手机访问pc网站自动跳转手机端网站代码
2020/12/24 Javascript
JQuery中属性过滤选择器用法实例分析
2015/05/18 Javascript
在Javascript中处理数组之toSource()方法的使用
2015/06/09 Javascript
JavaScript Length 属性的总结
2015/11/02 Javascript
vue.js 1.x与2.0中js实时监听input值的变化
2017/03/15 Javascript
使用vue-cli打包过程中的步骤以及问题的解决
2018/05/08 Javascript
JavaScript类的继承方法小结【组合继承分析】
2018/07/11 Javascript
jQuery.extend 与 jQuery.fn.extend的用法及区别实例分析
2018/07/25 jQuery
kafka调试中遇到Connection to node -1 could not be established. Broker may not be available.
2019/09/17 Javascript
layui--select使用以及下拉框实现键盘选择的例子
2019/09/24 Javascript
Vue自动构建发布脚本的方法示例
2020/07/24 Javascript
[02:51]2014DOTA2 TI小组赛总结中国军团全部进军钥匙球馆
2014/07/15 DOTA
Python检测数据类型的方法总结
2019/05/20 Python
Python3.8中使用f-strings调试
2019/05/22 Python
django基于restframework的CBV封装详解
2019/08/08 Python
python支持多继承吗
2020/06/19 Python
Python的信号库Blinker用法详解
2020/12/31 Python
Nike瑞典官方网站:Nike.com (SE)
2018/11/26 全球购物
临床医学系毕业生推荐信
2013/11/09 职场文书
绿色学校实施方案
2014/03/31 职场文书
文明礼貌演讲稿
2014/05/12 职场文书
企业投资意向书
2015/05/09 职场文书
Python机器学习实战之k-近邻算法的实现
2021/11/27 Python