使用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 相关文章推荐
兼容多浏览器的字幕特效Marquee的通用js类
Jul 20 Javascript
js onpropertychange输入框 事件获取属性
Mar 26 Javascript
javascript实现的在当前窗口中漂浮框的代码
Mar 15 Javascript
js纯数字逐一停止显示效果的实现代码
Mar 16 Javascript
设置点击文本框或图片弹出日历控件的实现代码
May 12 Javascript
jQuery实现的可编辑表格完整实例
Jun 20 Javascript
微信小程序 Tab页切换更新数据
Jan 05 Javascript
angular1配合gulp和bower的使用教程
Jan 19 Javascript
angular动态表单制作
Feb 23 Javascript
vue keep-alive 动态删除组件缓存的例子
Nov 04 Javascript
JS实现商品橱窗特效
Jan 09 Javascript
vue使用nprogress加载路由进度条的方法
Jun 04 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反弹shell实现代码
2009/04/22 PHP
PHP实现抓取HTTPS内容
2014/12/01 PHP
js 鼠标拖动对象 可让任何div实现拖动效果
2009/11/09 Javascript
jqPlot 基于jquery的画图插件
2011/04/26 Javascript
基于jquery的拖动布局插件
2011/11/25 Javascript
js数组操作学习总结
2013/11/04 Javascript
结合JQ1.9通过js正则判断各种浏览器版本的方法
2013/12/30 Javascript
Javascript this 函数深入详解
2016/12/13 Javascript
详解JavaScript RegExp对象
2017/02/04 Javascript
AngularJS路由切换实现方法分析
2017/03/17 Javascript
Vue引入sass并配置全局变量的方法
2018/06/27 Javascript
jQuery实现的网站banner图片无缝轮播效果完整实例
2019/01/28 jQuery
如何用原生js写一个弹窗消息提醒插件
2019/05/24 Javascript
解决node终端下运行js文件不支持ES6语法
2020/04/04 Javascript
Python批量修改文件后缀的方法
2014/01/26 Python
java直接调用python脚本的例子
2014/02/16 Python
python中偏函数partial用法实例分析
2015/07/08 Python
Python基于identicon库创建类似Github上用的头像功能
2017/09/25 Python
详解python中的装饰器
2018/07/10 Python
matplotlib.pyplot绘图显示控制方法
2019/01/15 Python
Django框架验证码用法实例分析
2019/05/10 Python
Python Numpy库常见用法入门教程
2020/01/16 Python
美国折扣宠物药房:Total Pet Supply
2018/05/27 全球购物
巴西网上药店:Drogaria Araujo
2021/01/06 全球购物
师范生实习个人的自我评价
2013/09/28 职场文书
国家励志奖学金获奖感言
2014/01/09 职场文书
给领导的致歉信范文
2014/01/13 职场文书
师德师风个人反思
2014/04/28 职场文书
机电一体化毕业生自荐信
2014/06/19 职场文书
2014年党风建设工作总结
2014/11/19 职场文书
2014年化工厂工作总结
2014/11/25 职场文书
Redis如何一键部署脚本
2021/04/12 Redis
python实现自动清理文件夹旧文件
2021/05/10 Python
关于nginx 实现jira反向代理的问题
2021/09/25 Servers
Redis调用Lua脚本及使用场景快速掌握
2022/03/16 Redis
nginx location 带斜杠【 / 】与不带的区别
2022/04/13 Servers