使用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 相关文章推荐
如何实现iframe(嵌入式帧)的自适应高度
Jul 26 Javascript
Javascript代码混淆综合解决方案-Javascript在线混淆器
Dec 18 Javascript
JMenuTab简单使用说明
Mar 13 Javascript
JS中数组Array的用法示例介绍
Feb 20 Javascript
js写出遮罩层登陆框和对联广告并自动跟随滚动条滚动
Apr 29 Javascript
JQuery实现当鼠标停留在某区域3秒后自动执行
Sep 09 Javascript
jquery实现具有收缩功能的垂直导航菜单
Feb 16 Javascript
解决vue2.0动态绑定图片src属性值初始化时报错的问题
Mar 14 Javascript
利用npm 安装删除模块的方法
May 15 Javascript
vue实现的仿淘宝购物车功能详解
Jan 27 Javascript
微信小程序下拉菜单效果的实例代码
May 14 Javascript
使用 Vue 实现一个虚拟列表的方法
Aug 20 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
全国FM电台频率大全 - 17 湖北省
2020/03/11 无线电
PHP 应用程序的安全 -- 不能违反的四条安全规则
2006/11/26 PHP
PHP 在线翻译函数代码
2009/05/07 PHP
php中3种方法统计字符串中每种字符的个数并排序
2012/08/27 PHP
PHP实现的mysql主从数据库状态检测功能示例
2017/07/20 PHP
php通过pecl方式安装扩展的实例讲解
2018/02/02 PHP
详细解读php的命名空间(二)
2018/02/21 PHP
IE8提示Invalid procedure call or argument 异常的解决方法
2012/09/30 Javascript
一个JS的日期格式化算法示例
2013/07/31 Javascript
jQuery如何实现点击页面获得当前点击元素的id或其他信息
2014/01/09 Javascript
JQuery each()嵌套使用小结
2014/04/18 Javascript
js使用ajax读博客rss示例
2014/05/06 Javascript
jQuery使用post方法提交数据实例
2015/03/25 Javascript
js实现商城星星评分的效果
2015/12/29 Javascript
JS for...in 遍历语句用法实例分析
2016/08/24 Javascript
nodejs基础知识
2017/02/03 NodeJs
AngularJS实现动态添加Option的方法
2017/05/17 Javascript
深入理解JavaScript的值传递和引用传递
2018/10/24 Javascript
VUE 动态组件的应用案例分析
2019/12/02 Javascript
jQuery 实现扁平式小清新导航
2020/07/07 jQuery
解决vue prop传值default属性如何使用,为何不生效的问题
2020/09/21 Javascript
[00:59]DOTA2荣耀之路1:Doom is back!weapon X!
2018/05/22 DOTA
python读写csv文件方法详细总结
2019/07/05 Python
python实现广度优先搜索过程解析
2019/10/19 Python
matplotlib绘制多个子图(subplot)的方法
2019/12/03 Python
传统HTML页面实现模块化加载的方法
2018/10/15 HTML / CSS
彼得罗夫美国官网:Peter Thomas Roth美国(青瓜面膜)
2017/11/05 全球购物
联想新西兰官方网站:Lenovo New Zealand
2018/10/30 全球购物
JAVA和C++的区别
2013/10/06 面试题
项目合作计划书
2014/01/09 职场文书
安全检查与奖惩制度
2014/01/23 职场文书
中专生毕业个人鉴定
2014/02/26 职场文书
政风行风评议工作总结
2014/10/21 职场文书
工厂见习报告范文
2014/10/31 职场文书
警示教育观后感
2015/06/17 职场文书
Python PIL按比例裁剪图片
2022/05/11 Python