类数组 Array-Like Objects 初探
Array-Like
javascript 中一切皆为对象,Array-Like 顾名思义就是类数组对象,和数组一样可以使用 []
操作符访问,有length
属性,但是没有数组的部分内置方法,譬如 push 、pop 等,最常见的类数组对象便是函数形参的 arguments,
那么,什么样的元素是 Array-Like Objects?
以下是 Javascript 权威指南上对它的定义
1 | function isArrayLike(o) { |
以上是对类数组的严格定义,以下是 underscore 中对类数组的定义:
1 | var MAX_ARRAY_INDEX = Math.pow(2, 53) - 1; |
很简单,不是数组,但是有 length 属性,且属性值为非负 Number 类型即可。至于 length 属性的值,underscore 给出了一个上限值 MAX_ARRAY_INDEX, 这是 javascript 中所能表示的最大数字。
想想还有什么同时能满足以上条件的?NodeList,HTML Collections,仔细想想,甚至还有字符串,或者拥有 length 属性的对象,函数(length 属性值为形参数量),等等。
Array-Like to Array
有的时候我们需要把 Array-Like Objects 转化为 Array 使之能使用数组的一些方法,以下是一些 Array-Like to Array 的转化方法
第一种最简单粗暴,直接新建一个数组,然后把类数组里边的元素一个一个赋值给临时数组,并返回临时数组
1
2
3
4
5
6
7function toArray(array_like) {
var tmp = [];
for (var i = 0; i < array_like.length; i++) {
tmp[i] = array_like[i];
}
return tmp;
}但这不是最优雅的,我们可以用数组内置的方法来实现
1
2
3function toArray(array_like) {
return Array.prototype.slice.call(array_like);
}我们也可以用
[]
来代替 Array.prototype 这样可以省几个字节,但是[]
会首先创建一个空数组,效率方便不及前者。1
2
3function toArray(array_like) {
return [].slice.call(array_like);
}
为什么这样可以转换?我们简单了解下,主要的原因是 slice 方法只需要参数有 length 属性即可。首先,slice 方法得到的结果是一个 新的数组,通过 Array.prototype.slice.call 传入的参数(假设为 a),如果没有 length 属性,或者 length 属性值不是 Number 类型,或者为负,那么直接返回一个空数组,否则返回 a[0]-a[length-1] 组成的数组 具体可以参考V8源代码
ES6 中给予了我们额外的 Array-Like to Array 的方式
1
2var str = "helloworld";
var arr = Array.from(str);
compatibility
IE 下 Array.prototype.slice.call(nodes) 会抛出错误 (because a DOM NodeList is not a JavaScript object)所以我们需要手动处理兼容性问题
1
2
3
4
5
6
7
8
9
10
11
12
13
function toArray(array_like) {
var res = [];
try {
res = Array.prototype.slice.call(array_like);
return res;
} catch (err) {
res = [];
for (var i = 0, length = array_like.length; i < length; i++) {
res.push(array_like[i]);
}
return res;
}
}
Others
数组的一些内置方法也可以接受类数组作为实参
1 | var obj = {0: 4, length: 2}; |