对Javascript 类数组对象的理解与应用 | Arguments与Nodelist

这篇文章将要告诉你:

  1. 什么是类数组对象
  2. 类数组对象是如何出现的
  3. 如何避免使用类数组对象的常见问题
  4. 将类数组对象转换为普通对象的方法

类数组对象

类数组(Array-like Object)对象是 javascript 中比较冷门的内容,如果不了解的话,会被坑到。
类数组对象 foo 有以下两个陷阱:

  1. 有 foo.length 属性,却没有 foo.push() 方法
  2. 和 for-in 一起使用会有坑

类数组对象的主要使用/产生场景有以下两种:

  1. 函数参数 arguments
  2. 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. [1, 2, "aa", callee: function, Symbol(Symbol.iterator): function]
  2. 3
  3. function a(){……(整个函数,此处省略)
  4. [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 的打印结果真是令人目瞪口呆:


对Javascript 类数组对象的理解与应用 | Arguments与Nodelist_第1张图片
除了0,1,2, 其他的是什么鬼?

原理是这样的,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
    }
};
终于打印出正常的结果了

当然,最好的办法就是:

  1. 使用for循环来遍历类数组对象
  2. 使用for循环来遍历类数组对象
  3. 使用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],符合预期

参考文章:

  1. MDN - querySelecorAll
  2. MDN - arguments
  3. MDN - Array.prototype.slice

你可能感兴趣的:(对Javascript 类数组对象的理解与应用 | Arguments与Nodelist)