所以你想要的length
东西就像{0: "first", 100: "last"}
是2
而不是101
,对吧?也就是说,稀疏数组length
之类的值预计会小于您从其属性中获得的值。没有内置方法可以为您做到这一点,因此过滤属性可能是最简单的方法。length
假设是这样:
“数字”键(如您所知实际上是字符串)的更好过滤器是这样的:
Object.keys(this).filter(k => (""+(+k))===k).length
过滤器将密钥强制转换为数字,然后返回字符串......如果它在往返过程中毫发无损,那么该密钥与数字键无法区分。否则,它不是真正的“数字”键,您将无法使用数字索引访问该属性。例如:
const obj: { [k: number]: string | undefined } = {};
obj[1] = "A";
console.log(JSON.stringify(obj)); // {"1":"A"}
obj[+1] = "B";
console.log(JSON.stringify(obj)); // {"1":"B"}
obj["1"] = "C";
console.log(JSON.stringify(obj)); // {"1":"C"}
obj["+1"] = "D";
console.log(JSON.stringify(obj)); // {"1":"C","+1":"D"}
console.log(Object.keys(obj).filter(o => !isNaN(parseInt(o, 10))));
// Array [ "1", "+1" ]
console.log(Object.keys(obj).filter(k => ("" + (+k)) === k));
// Array [ "1" ]
即使数值与数值1
相同+1
,字符串值"1"
也不同于字符串值"+1"
。该对象obj
在键处具有属性,"1"
但"+1"
您只能使用"1"
数字索引访问该属性。
请注意,数字索引也不会将您限制为非负整数键。以下所有内容在 TypeScript 和 JavaScript 中都很好:
obj[1.5] = "E";
obj[-2] = "F";
obj[Infinity] = "G";
obj[NaN] = "H";
obj[1.23e100] = "I";
其中大多数未通过parseInt()
测试,但通过了我给出的过滤器。同样,类似的键"23 skidoo"
会通过我给出的过滤器但通过parseInt()
测试。
无论如何,希望这会有所帮助。祝你好运!
更新:由于您希望它更像一个实际数组,因此该length
属性应始终大于最高数字 index。看起来一个数组只关心与小于某个值的非负整数相对应的数字兼容索引(可能是 2^32)。您可以通过以下方式获得类似的行为:
get length() {
return Math.max(-1, ...Object.keys(this).filter(
k => ("" + (+k)) === k // numeric keys
).map(
k => +k // as numbers
).filter(
n => isFinite(n) && n >= 0 && n === Math.round(n) // non-neg integers
&& n < 4294967296 // less than 2^32
)) + 1;
}
你可以简化一点,但我认为它或多或少是你正在寻找的东西。
如果您宁愿从不同的角度来解决整个问题,您可以使您的类实例 aProxy
代表数字索引访问和length
真正的数组。或者更好的是,您可以使您的类成为其自身的子类Array
以继承的行为,length
而无需从头开始重新实现它。
再次祝你好运。