这篇文章将要告诉你:
- 什么是类数组对象
- 类数组对象是如何出现的
- 如何避免使用类数组对象的常见问题
- 将类数组对象转换为普通对象的方法
类数组对象
类数组(Array-like Object)对象是 javascript 中比较冷门的内容,如果不了解的话,会被坑到。
类数组对象 foo 有以下两个陷阱:
- 有 foo.length 属性,却没有 foo.push() 方法
- 和 for-in 一起使用会有坑
类数组对象的主要使用/产生场景有以下两种:
- 函数参数 arguments
- querySelectorAll 的结果
下面来分别说一下。
1. 类数组对象——函数参数arguments
function a(){
console.log(arguments); //----------------1. 函数的传入参数,类数组对象
console.log(arguments.length); //---------2. 传入参数的个数
console.log(arguments.callee); //---------3. 指向函数本身 a()
console.log(Object.prototype.toString.call(arguments)); //----4. 精确判断对象类型
}
a(1,2,'aa');
执行结果是:
- [1, 2, "aa", callee: function, Symbol(Symbol.iterator): function]
- 3
- function a(){……(整个函数,此处省略)
- [object Arguments]
从执行结果可以看出,arguments的长度是传入参数的个数,但它还包括callee这个属性,指向自身。callee可以用于递归,比如在a()内可以继续通过调用arguments.callee(3,4,5)
实现递归。
2. 类数组对象——querySelectorAll的结果
类数组对象应该使用var i = 0; i < a.length;i++
方式遍历,和for-in一起使用的时候就是个灾难啊。
下面我们来看一下灾难现场:我们有三个DOM:
第一段
第二段
第三段
然后我们使用querySelectorAll进行选择,使用for-in遍历:
var doms = document.querySelectorAll('.cls-p');
console.log(doms.length); // 3
for (var i in doms) {
console.log(i); // 打印doms中属性的key
}
for-in 的打印结果真是令人目瞪口呆:
原理是这样的,for-in 遍历的是元素的所有属性,包括原型链中的属性,所以会打印出keys, entries 这种原型链的属性。
console.log( 'keys' in doms) // true,'keys' 是 doms的属性
console.log(doms.hasOwnProperty('keys')) // false,‘keys’ 不是doms的自有属性
在上面的代码中,我们用了hasOwnProperty()
,这个方法用于判断当前属性是否是对象的自有属性。
那么我们可以用hasOwnProperty()
来填平for-in
的坑么?这是可以的。
for (var i in doms) {
if (doms.hasOwnProperty(i)) {
console.log(i); //打印doms中自有属性的key
}
};
当然,最好的办法就是:
- 使用for循环来遍历类数组对象
- 使用for循环来遍历类数组对象
- 使用for循环来遍历类数组对象
将类数组对象转换为普通对象
文章前面提到过,类数组对象有 foo.length 属性,却没有 foo.push() 方法。通过将类数组对象转换为普通对象,可以使用push()方法。
方法一:for 循环复制到新对象中
function objToArr() {
// arguments.push[4];
// 如果运行上面的代码,会报错 "Uncaught TypeError: Cannot read property '4' of undefined"
var arr = [];
for (var i = 0, len = arguments.length; i < len; i++)
arr[i] = arguments[i];
return arr;
}
var arr = objToArr(1, 2, 3);
arr.push(4);
console.log(arr); // [1,2,3,4],符合预期
方法二:使用Array.prototype.slice方法
原理:slice() 不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。
function objToArr() {
// arguments.push[4];
// 如果运行上面的代码,会报错 "Uncaught TypeError: Cannot read property '4' of undefined"
var arr = Array.prototype.slice.call(arguments);
return arr;
}
var arr = objToArr(1, 2, 3);
arr.push(4);
console.log(arr); // [1,2,3,4],符合预期
参考文章:
- MDN - querySelecorAll
- MDN - arguments
- MDN - Array.prototype.slice