DOM节点列表转化成数组的几种方式

前段我们写了个油猴脚本:[例]简单脚本提取页面中所有图片地址(实现、改进、实战),里面需要将DOM节点列表转化成数组,今天我们来讨论一下将DOM节点列表转化为数组的几种方式。


for循环

循环是最直观,也是最可靠的,因为所有浏览器都支持for循环。但它有许多的变种:

// nl 变量为 DOM NodeList的简写
// arr 变量为 nl 将要转化成的数组
// 方式 1
var arr = [];
for(var i = 0, n; n = nl[i]; ++i) arr.push(n);

// 方式 2
var arr = []; // Will hold the array of Node's
for(var i = 0, ll = nl.length; i != ll; arr.push(nl[i++]));

// 方式 3
var arr = [];
for(var i = nl.length; i--; arr.unshift(nl[i]));

// 方式 4
var arr = [];
for(var i=-1,l=nl.length;++i!==l;arr[i]=nl[i]);


Array.prototype.slice.call方式

在javascrtipt中,call()和apply()可以用来重新定义函数的执行环境。它们的第一个参数是要调用函数的母对象,它是调用上下文,在函数体内通过this来获得对它的引用。如要想以对象o的来调用函数f(),可以这样:

f.call(o);
f.apply(o);


达到以下的效果:

o.m=f;
o.m();
delete o.m;


所以Array.prototype.slice.call(NodeList),相当于NodeList.slice();而得到一个于NodeList等长同项的数组对象。


原型增加forEach函数方式

在大多数场景下,将DOM节点列表转换成数组,无非是想用数组的隐式迭代的方法。可以经NodeList原型上直接实现forEach方法。

NodeList.prototype.forEach = Array.prototype.forEach;


Array.from()方式

回看Array.prototype.slice.call这种方式,因为不需要显示的写循环,一直被当成一种高级用法广泛使用。但是我们在调用它时不能直观的感受到是在“将arrayLike转换成一个数组”。

ES 6为了增加语义的清晰,语法的简洁性。添加了一个新方法Array.from(),用于将arrayLike的对象转换成数组。所以:

var arr = Array.from(nl)


数组/对象扩展运算符 …

扩展运算符用三个点号表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值。详细请参考:(…)这三个点在JavaScript中是个啥意思?

let arr = [...nl];


思考

这些方法中,有些是各浏览器通用的,有些高级浏览器适用的,那么哪种方式哪个浏览器性能高些,我们将做一个性能对比测试,请持续关注!