将HTMLCollection/NodeList/伪数组转换成数组
什么是伪数组
能通过Array.prototype.slice转换为真正的数组的带有length属性的对象。
这种对象有很多,比较特别的是arguments对象,document.forms,Form.elements,Select.options还有像调用getElementsByTagName,document.childNodes之类的,它们都返回NodeList对象都属于伪数组。
伪数组具有以下特点:
1,具有length属性
2,按索引方式存储数据
3,不具有数组的push,pop等方法
我们可以通过Array.prototype.slice.call(fakeArray)将伪数组转变为真正的Array对象。
来看个示例:
var fakeArray01 = {0:'a',1:'b',length:2};//这是一个标准的有伪数组对象 var arr01 = Array.prototype.slice.call(fakeArray01); alert(arr01[0]);//a var arr02 = [].slice.call(fakeArray01); alert(arr02[0]);//aslice 可以用来获取数组片段,它返回新数组,不会修改原数组。 示例中可以看到fakeArray被成功的转换成了Array对象。也许大家对Array.prototype.slice.call这种写法比较陌生,其实我们也可以通过[].slice.call这种形式实现同样的效果,那为什么我们要通过prototype的形式实现呢,答案是以prototype的形式执行程序效率更高,同样代码也更加优美。 **伪数组的实现** 让我们再深入的看一下伪数组的实现。 我们来看一些特殊的用例:
var fakeArray01 = {a:'a',b:'b',length:2};//没有length下标对应的值 var arr01 = Array.prototype.slice.call(fakeArray01); alert(arr01[0]);//undefined var fakeArray02 = {0:'a',1:'b',length:'num'};//length不是数值 var arr02 = Array.prototype.slice.call(fakeArray02); alert(arr02[1]);//undefined同样fakeArray01和fakeArray02被转换成了真正的数组,但是数组中的值都为undefined 查看 V8 引擎[ array.js](http://www.google.com/codesearch/p?hl=en#W9JxUuHYyMg/trunk/src/array.js) 的源码,可以将 slice 的内部实现简化为:
function slice(start, end) { var len = ToUint32(this.length), result = []; for(var i = start; i < end; i++) { result.push(this[i]); } return result; }可以看出,slice 并不需要 this 为 array 类型,只需要有 length 属性即可。并且 length 属性可以不为 number 类型,当不能转换为数值时,ToUnit32(this.length) 返回 0. 根据以上结论可以得出:fakeArray01被转换成了lenth为2的数组,其值都被初始化为undefined,fakeArray02被转换成了length为0的数组,自然访问下标为1的元素返回undefined **IE的问题** 针对于标准浏览器slice实现已经可以解释所有的问题,但是IE在处理NodeList时出现了问题。IE中无法将NodeList转换为真正的数组,会出错。这又是为什么呢?严格说,在IE内部定义了一个抽象类Arraioid,Array和Arguments都继承与此,所以可以用slice。但DOM对象是通过COM接入到JScript的,slice检测的时候失效。 最后给出将HTMLCollection/NodeList/伪数组转换成数组的兼容代码:
var makeArray = function(obj){ return Array.prototype.slice.call(obj,0); } try{ Array.prototype.slice.call(document.documentElement.childNodes, 0)[0].nodeType; }catch(e){ makeArray = function(obj){ var res = []; for(var i=0,len=obj.length; i<len; i++){ res.push(obj[i]); } return res; } }
整理自:
[http://www.cnblogs.com/fool/archive/2010/10/09/1846966.html](http://www.cnblogs.com/fool/archive/2010/10/09/1846966.html) [http://www.cnblogs.com/snandy/archive/2011/03/12/1981583.html](http://www.cnblogs.com/snandy/archive/2011/03/12/1981583.html)