使用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 URL参数判断,确定菜单样式
May 31 Javascript
js 对小数加法精度处理示例说明
Dec 27 Javascript
对于Form表单reset方法的新认识
Mar 05 Javascript
iframe调用父页面函数示例详解
Jul 17 Javascript
上传图片预览JS脚本 Input file图片预览的实现示例
Oct 23 Javascript
javascript中定义类的方法汇总
Dec 28 Javascript
JavaScript+html5 canvas绘制的圆弧荡秋千效果完整实例
Jan 26 Javascript
微信公众号 摇一摇周边功能开发
Dec 08 Javascript
微信小程序 图片宽度自适应的实现
Apr 06 Javascript
jQuery滚动插件scrollable.js用法分析
May 25 jQuery
node app 打包工具pkg的具体使用
Jan 17 Javascript
Vue使用Three.js加载glTF模型的方法详解
Jun 14 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检查日期函数checkdate用法实例
2015/03/19 PHP
wordpress自定义标签云与随机获取标签的方法详解
2019/03/22 PHP
Laravel-admin之修改操作日志的方法
2019/09/30 PHP
PHP二维数组分页2种实现方法解析
2020/07/09 PHP
分页栏的web标准实现
2011/11/01 Javascript
新发现一个骗链接的方法(js读取cookies)
2012/01/11 Javascript
学习从实践开始之jQuery插件开发 对话框插件开发
2012/04/26 Javascript
jquery实现图片渐变切换兼容ie6/Chrome/Firefox
2013/08/02 Javascript
随鼠标移动的时钟非常漂亮遗憾的是只支持IE
2014/08/12 Javascript
node.js中的http.response.writeHead方法使用说明
2014/12/14 Javascript
分享JavaScript与Java中MD5使用两个例子
2015/12/23 Javascript
jquery实用技巧之输入框提示语句
2016/07/28 Javascript
jQuery操作dom实现弹出页面遮罩层(web端和移动端阻止遮罩层的滑动)
2016/08/25 Javascript
在JS中a标签加入单击事件屏蔽href跳转页面
2016/12/16 Javascript
jQuery插件FusionWidgets实现的AngularGauge图效果示例【附demo源码】
2017/03/23 jQuery
令按钮悬浮在(手机)页面底部的实现方法
2017/05/02 Javascript
VUE实时监听元素距离顶部高度的操作
2020/07/29 Javascript
vue深度监听(监听对象和数组的改变)与立即执行监听实例
2020/09/04 Javascript
Python 专题一 函数的基础知识
2017/03/16 Python
windows下python之mysqldb模块安装方法
2017/09/07 Python
python2.7使用plotly绘制本地散点图和折线图
2019/04/02 Python
Python+selenium点击网页上指定坐标的实例
2019/07/05 Python
如何通过50行Python代码获取公众号全部文章
2019/07/12 Python
Linux下通过python获取本机ip方法示例
2019/09/06 Python
python3 pathlib库Path类方法总结
2019/12/26 Python
基于Python的接口自动化unittest测试框架和ddt数据驱动详解
2021/01/27 Python
VICHY薇姿美国官方网站:欧洲药房第一的抗衰老品牌
2017/11/22 全球购物
美国最流行的男士时尚网站:Touch of Modern
2018/02/05 全球购物
竞选演讲稿范文
2013/12/28 职场文书
大二法英学生职业生涯规划范文
2014/02/27 职场文书
2014年学习厉行节约反对浪费思想汇报
2014/09/10 职场文书
2015年发展党员工作总结报告
2015/03/31 职场文书
欠条格式范本
2015/07/03 职场文书
Python 制作自动化翻译工具
2021/04/25 Python
python正则表达式re.search()的基本使用教程
2021/05/21 Python
Vue vee-validate插件的简单使用
2021/06/22 Vue.js