1 类数组对象
JavaScript中有一种数据类型叫做类数组对象。它是一种神奇的数据类型,似数组而非数组,有很多与数组类似的用法,但是却不能使用数组的一些方法,比如最常见的arguments对象:
function test(){
console.log(arguments.length);//3
console.log(arguments[0]);//0
arguments.length = 1;
console.log(arguments[1]);//1
console.log(arguments.slice(0, 1));//arguments.slice is not a function
}
test(0, 1, 2)
复制代码
从上面的例子可以看出,类数组对象也有一个length属性,也可以通过中括号加索引的形式访问其“元素”。与数组不同的是,改变length属性并不能真正改变类数组对象的“长度”,而且类数组对象并不具有数组所有的方法。
那么它到底是个什么样的存在呢?
从名字得知,类数组对象首先是一个对象,它有一些与数组类似的特征:
- 它有一个数值类型的length属性
- 它有与length对应的非负整数属性
可以创建如下的类数组对象:
var arrLikeObj = {
length: 2,
0: 'a',
1: 'b'
}
复制代码
这样,就可以很容易解释上面类数组对象的特征了。
常见的类数组对象有arguments对象、通过Node.childNodes得到的NodeList对象、通过document.getElementsBy...得到的HTMLCollection对象、element.attributes得到的NamedNodeMap对象等。
2 类数组对象转化为数组
因为类数组对象只是一个普通的对象,而不是数组,因此我们不能直接在它上面调用一些数组特有的方法。虽然可以通过call来应用数组的方法,如:
var arrLikeObj = {
length: 4,
0: 'a',
2: 'c',
1: 'b',
3: 'd'
}
Array.prototype.sort.call(arrLikeObj);
//返回{0: "a", 1: "b", 2: "c", 3: "d", length: 4}
复制代码
但是很多时候我们还是需要将类数组对象转化为数组。下面提供三种方法。
2.1 Array.prototype.slice.call(arrLikeObj)
var arrLikeObj = {
length: 4,
0: 'a',
1: 'b',
2: 'c',
3: 'd'
}
var arr = Array.prototype.slice.call(arrLikeObj);
//arr为["a", "b", "c", "d"]
复制代码
使用数组对象的slice方法可以将类数组对象转化为数组对象。slice方法用于复制数组的一部分,为什么它会对类数组对象产生这样的效果呢?为了探寻其原因,我们可以看一看slice方法实现的源码:
// This will work for genuine arrays, array-like objects,
// NamedNodeMap (attributes, entities, notations),
// NodeList (e.g., getElementsByTagName), HTMLCollection (e.g., childNodes),
// and will not fail on other DOM objects (as do DOM elements in IE < 9)
Array.prototype.slice = function(begin, end) {
// IE < 9 gets unhappy with an undefined end argument
end = (typeof end !== 'undefined') ? end : this.length;
// For native Array objects, we use the native slice function
if (Object.prototype.toString.call(this) === '[object Array]'){
return _slice.call(this, begin, end);
}
// For array like object we handle it ourselves.
var i, cloned = [],
size, len = this.length;
// Handle negative value for "begin"
var start = begin || 0;
start = (start >= 0) ? start : Math.max(0, len + start);
// Handle negative value for "end"
var upTo = (typeof end == 'number') ? Math.min(end, len) : len;
if (end < 0) {
upTo = len + end;
}
// Actual expected size of the slice
size = upTo - start;
if (size > 0) {
cloned = new Array(size);
if (this.charAt) {
for (i = 0; i < size; i++) {
cloned[i] = this.charAt(start + i);
}
} else {
for (i = 0; i < size; i++) {
cloned[i] = this[start + i];
}
}
}
return cloned;
};
复制代码
代码逻辑并不复杂,对于真正的数组,会调用一个native function,而对于类数组对象,则会走下面的逻辑,返回一个真正的数组对象(这里参考了这篇博客)。
2.2 Array.from()
ES6提供了Array.from方法,用于将部署了Iterator接口的对象(可遍历对象,如Set、Map、String等)和类数组对象转化为数组。
var arrLikeObj = {
length: 4,
0: 'a',
1: 'b',
2: 'c',
3: 'd'
}
var arr = Array.from(arrLikeObj);
//arr为["a", "b", "c", "d"]
复制代码
2.3 扩展运算符...
ES6提供的扩展运算符...可以将可遍历对象转化为由逗号分隔的序列。
[...'abcd'] //["a", "b", "c", "d"]
复制代码
js提供的一些原生类数组对象(如:arguments对象、NodeList对象、HTMLCollection对象等)是部署了Iterator接口的(即是可遍历的),因此也可以用这个方法来进行转换。
function test(){
console.log([...arguments]);//[0, 1, 2]
}
test(0,1,2)
复制代码
注意,这个方法只对部署了Iterator接口的类数组对象有效。
总结
这篇文章主要介绍了类数组对象以及将类数组对象转化为数组对象的三种方法。如果有不对的地方,欢迎指正。