数组越界:与未定义的比较,或长度检查

Array Out of Bounds: Comparison with undefined, or length check?

本文关键字:检查 比较 越界 未定义 数组      更新时间:2023-09-26

这似乎是一个常见的javascript习语:

function foo (array, index) {
    if (typeof array[index] == 'undefined')
        alert ('out of bounds baby');
}

相对于更流行的(在其他语言中)和概念上更简单的

function foo (array, index) {
    if (index >= array.length)
        alert ('boo');
}

我知道第一种情况也适用于其中有"间隙"的数组,但是这是一种足够常见的情况来保证习语吗?

提示这个问题的代码示例可以在这里看到。在这种情况下,当使用'参数'变量内部的函数,是不是明智的假设,它将是一个连续的数组?

唯一正确的方法是检查索引和长度。

元素可以赋值为undefined。在这里用它做哨兵实在是太愚蠢了。(检查未定义可能有其他有效且可能重叠的原因,但不是"越界检查"——当给定参数的值确实 undefined时,另一个问题中的代码将显示有争议的错误结果。)

快乐编码。

你也可以这样写:

if (index in array) {

,即使array[index]设置为undefined,也将返回true。

不要测试undefined。您应该使用数组的长度。在某些情况下,测试undefined根本不起作用,因为undefined是合法数组条目的合法值。这是一个合法的JS数组:

var legalArray = [4, undefined, "foo"];

你可以像这样访问它:

var legalArray = [4, undefined, "foo"];
var result = "";
for (var i = 0; i < legalArray.length; i++) {
    result += legalArray[i] + "<br>";
}
$("#result").html(result);

生成如下输出:

4
undefined
foo

如下所示:http://jsfiddle.net/jfriend00/J5PPe/

据我所知,这并不常见,更常见的是:

for (var i=0, iLen=array.length; i<iLen; i++) {
  // do stuff
}

不应该与undefined进行比较,因为数组的成员可能被赋值为undefined,也可能没有被赋值。

var a = [0,,,,];
alert(a.length); // 4 (or 5 in buggy IE);
a[1] === undefined; // true but not out of bounds

使用for循环的主要原因是,如果使用for..in循环,数组属性可能不会按数字顺序返回。

for..in循环在稀疏数组中效率更高,但如果有必要,必须处理可能的乱序访问(如必须避免继承和非数字可枚举属性)。

在JavaScript中数组可以是稀疏的——它们可以包含"空洞"。例如

const array = new Array(3);

导致array为三个"孔"。-不是值。因此,尽管

const isInBounds = 0 <= index && index < array.length;

正确识别index是否在array的边界内,但不指示array[index]是否有值。

Object.prototype.hasOwnProperty()可用于确定index中是否存在值。还需要注意的是,语言的不同部分在"洞"的存在下会表现得非常不同。

// ESLint: no-prototype-builtins)
const hasOwnProperty = Object.prototype.hasOwnProperty;
function show(array, index) {
  const msg =
    0 > index || index >= array.length
      ? `index ${index} is out of bounds`
      : !hasOwnProperty.call(array, index)
      ? `index ${index} is a hole`
      : `index ${index} holds ${array[index]}`;
  console.log(msg);
}
const subject = [undefined, , 1];
show(subject, -1);
// "index -1 is out of bounds"
for (let i = 0; i < subject.length; i += 1) show(subject, i);
// "index 0 holds undefined"
// "index 1 is a hole"
// "index 2 holds 1"
show(subject, 3);
// "index 3 is out of bounds"
const toString = (value) =>
  value !== undefined ? value.toString() : 'undefined';
// for..of doesn't skip holes
const byForOf = [];
for (const value of subject) byForOf.push(toString(value));
console.log(`Values found by for..of: ${byForOf.join(', ')}`);
// "Values found by for..of: undefined, undefined, 1"
// .forEach skips holes
const byForEach = [];
subject.forEach((value) => byForEach.push(toString(value)));
console.log(`Values found by .forEach: ${byForEach.join(', ')}`);
// "Values found by .forEach: undefined, 1"
// .reduce skips holes
const reducer = (acc, value) => {
  acc.push(toString(value));
  return acc;
};
const byReduce = subject.reduce(reducer, []);
console.log(`Values found by .reduce: ${byReduce.join(', ')}`);
// "Values found by .reduce: undefined, 1"
// .map preserves holes
const byMap = subject.map(toString);
console.log(`Values found by .map: ${byMap.join(', ')}`);
// "Values found by .map: undefined, , 1"

在这种情况下,测试它以确保它不会意外地将字符串"undefined"添加到调用字符串中。在下面的例子中,它就会这样做:

var undefd;
"{0} is dead, but {1} is alive! {0} {2}".format("ASP", undefd, "ASP.NET")
// output as "ASP is dead, but {1} is alive! ASP ASP.NET"

就我个人而言,我可能会简单地缓存长度,然后进行数值比较。

编辑

旁注:他的方法也避免了NaN检查,但强制执行严格的并行:

// this will fail unless 0001 is cast to a number, which means the method
// provided will fail. 
"{0} is dead, but {1} is alive! {0001} {2}".format("ASP", "ASP.NET")