使用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 05 Javascript
javascript编写实用的省市选择器
Feb 12 Javascript
在Web项目中引入Jquery插件报错的完美解决方案(图解)
Sep 19 Javascript
jquery动态创建div与input的实例代码
Oct 12 Javascript
HTML的select控件美化
Mar 27 Javascript
js中apply()和call()的区别与用法实例分析
Aug 14 Javascript
vue自定义js图片碎片轮播图切换效果的实现代码
Apr 28 Javascript
浅谈目前可以使用ES10的5个新特性
Jun 25 Javascript
JS浮点数运算结果不精确的Bug解决
Aug 01 Javascript
vue Tab切换以及缓存页面处理的几种方式
Nov 05 Javascript
Vue 技巧之控制父类的 slot
Feb 24 Javascript
Vue封装全局过滤器Filters的步骤
Sep 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 max_execution_time执行时间问题
2011/07/17 PHP
php后台多用户权限组思路与实现程序代码分享
2012/02/13 PHP
php中smarty实现多模版网站的方法
2015/06/11 PHP
Django中通过定时任务触发页面静态化的处理方式
2018/08/29 PHP
PHP函数积累总结
2019/03/19 PHP
解决laravel session失效的问题
2019/10/14 PHP
js 提交和设置表单的值
2008/12/19 Javascript
jQuery select操作控制方法小结
2010/05/26 Javascript
javascript学习之闭包分析
2010/12/02 Javascript
JS对img标签进行优化使用onerror显示默认图像
2014/04/24 Javascript
AngularJs基于角色的前端访问控制的实现
2016/11/07 Javascript
js实现五星评价功能
2017/03/08 Javascript
javascript观察者模式实现自动刷新效果
2017/09/05 Javascript
vue移动端实现下拉刷新
2018/04/22 Javascript
vue中的router-view组件的使用教程
2018/10/23 Javascript
微信公众号中的JSSDK接入及invalid signature等常见错误问题分析(全面解析)
2020/04/11 Javascript
Python中用于转换字母为小写的lower()方法使用简介
2015/05/19 Python
python 3.6.2 安装配置方法图文教程
2018/09/18 Python
Tensorflow分类器项目自定义数据读入的实现
2019/02/05 Python
使用Python自动化破解自定义字体混淆信息的方法实例
2019/02/13 Python
简单了解python装饰器原理及使用方法
2019/12/18 Python
通俗讲解python 装饰器
2020/09/07 Python
python Paramiko使用示例
2020/09/21 Python
Python实现简单的猜单词小游戏
2020/10/28 Python
python实现一个简单RPC框架的示例
2020/10/28 Python
使用Python获取爱奇艺电视剧弹幕数据的示例代码
2021/01/12 Python
python爬虫智能翻页批量下载文件的实例详解
2021/02/02 Python
HTML5 FormData 方法介绍以及实现文件上传示例
2017/09/12 HTML / CSS
html5组织内容_动力节点Java学院整理
2017/07/10 HTML / CSS
eDreams巴西:廉价机票,酒店优惠和度假套餐
2017/04/14 全球购物
什么是会话Bean
2015/05/14 面试题
论文评语大全
2014/04/29 职场文书
征用土地赔偿协议书
2014/09/26 职场文书
八年级英语教学反思
2016/02/15 职场文书
python某漫画app逆向
2021/03/31 Python
HTML实现仿Windows桌面主题特效的实现
2022/06/28 HTML / CSS