使用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 相关文章推荐
获取JavaScript用户自定义类的类名称的代码
Mar 08 Javascript
js cookies 常见网页木马挂马代码 24小时只加载一次
Apr 13 Javascript
javascript 在网页中的运用(asp.net)
Nov 23 Javascript
jquery中get和post的简单实例
Feb 04 Javascript
js实现宇宙星空背景效果的方法
Mar 03 Javascript
将html页面保存成图片,图片写入pdf的实现方法(推荐)
Sep 17 Javascript
javascript学习之json入门
Dec 22 Javascript
JS基于正则表达式实现的密码强度验证功能示例
Sep 21 Javascript
你应该知道的几类npm依赖包管理详解
Oct 06 Javascript
javascript+html5+css3自定义弹出窗口效果
Oct 26 Javascript
vue template中slot-scope/scope的使用方法
Sep 06 Javascript
微信小程序官方动态自定义底部tabBar的例子
Sep 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编码转换
2012/11/05 PHP
PHP模板引擎Smarty之配置文件在模板变量中的使用方法示例
2016/04/11 PHP
向fckeditor编辑器插入指定代码的方法
2007/05/25 Javascript
javascript 快速排序函数代码
2012/05/30 Javascript
jquery判断至少有一个checkbox被选中的方法
2015/06/05 Javascript
javascript创建函数的20种方式汇总
2015/06/23 Javascript
jquery trigger函数执行两次的解决方法
2016/02/29 Javascript
jquery插件autocomplete用法示例
2016/07/01 Javascript
JS中用try catch对代码运行的性能影响分析
2016/12/26 Javascript
原生js+cookie实现购物车功能的方法分析
2017/12/21 Javascript
vue-cli实现多页面多路由的示例代码
2018/01/30 Javascript
JavaScript事件发布/订阅模式原理与用法分析
2018/08/21 Javascript
jQuery pagination分页示例详解
2018/10/23 jQuery
Vue 使用计时器实现跑马灯效果的实例代码
2019/07/11 Javascript
javascript导出csv文件(excel)的方法示例
2019/08/25 Javascript
在layer弹层layer.prompt中,修改placeholder的实现方法
2019/09/27 Javascript
关于AngularJS中几种Providers的区别总结
2020/05/17 Javascript
element-ui 弹窗组件封装的步骤
2021/01/22 Javascript
[00:52]DOTA2第二届亚洲邀请赛预选赛宣传片
2017/01/13 DOTA
Python的print用法示例
2014/02/11 Python
python使用 HTMLTestRunner.py生成测试报告
2017/10/20 Python
Python实现的自定义多线程多进程类示例
2018/03/23 Python
Python csv模块使用方法代码实例
2019/08/29 Python
python如何从键盘获取输入实例
2020/06/18 Python
web字体加载方案优化小结
2019/11/29 HTML / CSS
澳大利亚珠宝商:Shiels
2019/10/06 全球购物
俄罗斯运动、健康和美容产品在线商店:Lactomin.ru
2020/07/23 全球购物
竞争上岗演讲稿范文
2014/05/12 职场文书
大学生社会实践活动总结
2014/07/03 职场文书
施工员岗位职责
2015/02/10 职场文书
行政人事专员岗位职责
2015/04/07 职场文书
运动会闭幕式通讯稿
2015/07/18 职场文书
学生会干部任命书
2015/09/21 职场文书
导游词之山海关
2019/12/10 职场文书
详解nginx location指令
2022/01/18 Servers
Nginx如何配置根据路径转发详解
2022/07/23 Servers