首页 > 解决方案 > 如何在javascript中迭代数组(不是元素)的属性?

问题描述

如果我有一个数组

let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';

在这种情况下,如何仅迭代属性 (prop1prop2)?

标签: javascriptecmascript-6

解决方案


我不知道有任何方法可以直接迭代普通属性。但是,您可以构造一个仅包含普通属性的列表,如下所示:

ar.keys()只给你数组元素索引(不是普通属性)

Object.keys(ar)为您提供所有属性和数组索引。

因此,您可以从所有属性和索引开始,然后过滤掉作为数组元素索引的那些,只留下属性。

let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';

let arrayIndexes = new Set(Array.from(ar.keys(), i => "" + i));
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));

let props = Object.keys(ar).filter(k => !arrayIndexes.has(k));
console.log("props", props);

或者,具有较少临时对象的相同概念:

let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';

let arrayIndexes = new Set();
for (let i of ar.keys()) {
    arrayIndexes.add("" + i);
}
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));

let props = Object.keys(ar).filter(k => !arrayIndexes.has(k));
console.log("props", props);

如果你想要所有自己的属性,包括不可枚举的属性,你可以这样做:

let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';

let arrayIndexes = new Set();
for (let i of ar.keys()) {
    arrayIndexes.add("" + i);
}
console.log("arrayIndexes (converted to string)", Array.from(arrayIndexes));

let props2 = Object.keys(Object.getOwnPropertyDescriptors(ar)).filter(k => !arrayIndexes.has(k));
console.log("props2", props2);

检查属性名称中的字符

注意:我尝试了一种解决方案,它迭代所有属性(包括数组索引)并通过检查属性名称中的字符过滤掉将被解释为数组索引的内容。事实证明这很麻烦。这不仅仅是它是否是数值或转换为数字那么简单。要被视为数组索引,这是我迄今为止发现的:

  1. 它必须只包含数字 0-9(没有加号或减号)
  2. 它必须通过Number.isSafeInteger(),因为它可以是一个数字,但它太大而不能成为一个整数而不会损失一些精度。转换为数字时,它也必须小于ar.length
  3. 属性名称不能以“0”开头,除非它只是“0”。例如 x["01"] 将不会被视为等效于 的数组索引x[1]

以下是一些不被解释为数组索引的类似数字的字符串:

"01"
"00"
"9999999999999999999999999999999"
"-1"
"-0"
"1.0"

这是一个这样的测试,虽然我觉得它并不比上面的测试简单:

let ar = ['a', 'b', 'c'];
ar.prop1 = 'Hello';
ar.prop2 = 'world';
ar["-1"] = 'minus one';
ar["0"] = 'a';
ar["-0"] = 'z';
ar["01"] = 'zzzz';
ar["1.0"] = 'aaa';
ar["999"] = 'Z';
ar["99999999999999999999999999999999"] = 'ZZ';
ar["+0"] = 'bbb';

let props = Object.keys(ar).filter(k => {
    // a property that isn't "0", but starts with "0" will not be an array index
    // for example, "01" is not an array index
    if (k.length > 1 && k.startsWith("0")) {
        return true;
    }
    // if not entirely made up of digits 0-9, keep it
    if (!/^\d+$/.test(k)) {
        return true;
    }
    // convert to number
    let index = +k;
    // if it's not a safe integer or it's longer than the length,
    // then it must not be an array index
    if (!Number.isSafeInteger(index) || index >= ar.length) {
        return true;
    }
    // if it passed all these tests, then it must be an array index so filter it out
    return false;

});
console.log("props", props);


推荐阅读