在JavaScript中有一种数据结构叫类数组,也被称为伪数组,伪数组并不是真正意义上的数组,但是它具有很多和数组相似的特性,所以被人们称为类数组。常见的伪数组:
如:function 内的arguments,通过document.forms、form.elements、document.links、select.options 、document.getElementsByName 、document.getElementsByTagName 、 children等方式获取的节点集合 .
当然你也可以写一个自定义的类数组对象:
var arryLike = {
0 : “a”,
1 : “b”,
2 : “c”,
length : “3”
}
类数组对象是一个很好的存储结构,但是它供能有限。我们可以通过如下例子看出类数组的局限性:
function fun(a,b,c) {
console.log(arguments.length); // 3
for(var i=0;i<arguments.length;i++)
{
console.log(arguments[i]); //1,2,3
}
console.log(arguments.pop());
//Uncaught TypeError : arguments.pop isnot a function
}
fun(1,2,3);
伪数组具有数组的长度,也可以使用数组的方法进行遍历,可是伪数组却无法使用数组自带的一些方法。因为它并不是真正意义上的数组对象实例。
console.log(arguments instanceof Array); //false
要想更加便捷的去操作这些类数组中的数据,我们在使用它们之前需要将他们转换为真正的数组。
对类数组进行转换最简单的方法是使用 [].slice.call ,但旧版本IE下的HTMLCollection 、NodeList 不是Object的子类 , 采用上边的方法会导致IE 执行异常。
下面是几个类库对类数组的处理方法,我们可以借鉴 。
1. jQuery的makeArray 方法
var makeArray = function (array) {
var ret = [];
if(array != null)
{
var i = array.length;
// window对象 ,字符串,函数,也有length 属性
if(i==null || typeofarray==="string" ||jQuery.isFunction(array) || array.setInterval )
ret[0] = array; // 非类数组的数组转换
else
while (i) // 类数组转换为数组
ret[--i] =array[i];
}
return ret;
}
jQuery的这个makeArray 方法功能比较强大,几乎可以将所有数据转为数组,伪数组的转换当然也不再话下。
2. Prototype.js 的$A 方法 :
function $A(iterable) {
if(!iterable)
return [];
if(iterable.toArray)
return iterable.toArray();
var length =iterable.length ||0,results = new Array(length);
while (length--)
results[length] = iterable[length];
return results;
}
可以看出Prototype 的 $A 方法代码比较简单清晰,利用了toArray方法和深度复制 。
3. Ext 的toArray 方法
var toArray = function () {
returnWindow.VBArray ?
function(a,i,j,res) {
res = [];
Ext.each(a,function (v) {
res.push(v);
})
returnres.slice(i||0,j||res.length);
}:
function(a,i,j) {
returnArray.prototype.slice.call(a,i||0,j||a.length);
}
}();
我们可以看出Ext的toArray 方法功能比较强大,它一开始就自动执行本身,以后就不需再判断浏览器了。其中 ,window.VBArray 是IE下特有的方法用来鉴别IE 浏览器 。此方法比较灵活,允许传入后续的参数,用来获取转义后的数组的某一部分而不是每次必须返回整个数组(实际是在将为伪数组转化为数组后进一步使用了数组的slice方法)。 其中参数a代表传进来的伪数组,参数i代表从第几个元素开始截取,j表示要截取的长度 。res 参数默认缺省 。
以上的几种方法分别具有各自的特点,但都能将伪数组转换为真正的数组 。实现的基本思路是,在非ie 下使用[].slice.call() 方法,在ie 下使用遍历复制,将伪数组中的元素复制到一个空数组中 。
你可能会疑惑为什么不直接使用遍历复制去实现数组转换,而非要去进行一下判断使用[].slice.call呢 ? 这是因为如果你使用复制的思路去实现伪数组转换,你将创建一个新的数组对象,这将消耗部分内存,而且使用这种方法转化出来的数组对象与我们之前的类数组对象并不是同一个索引,也就是说这其实并不算是真正意义上的转化 。所以最好当我们的[].slice.call()方法不能奏效时 ,你再考虑使用这种思路 。