【问题标题】:Is it safe to use 'undefined' as sentinel in a Javascript while loop?在 Javascript while 循环中使用“未定义”作为标记是否安全?
【发布时间】:2019-10-23 21:25:54
【问题描述】:

在 Javascript 中使用这种循环是否安全?

denseArray = [1,2,3,4,5, '...', 99999]

var x, i = 0
while (x = denseArray[i++]) {
    document.write(x + '<br>')  
    console.log(x)  
}

document.write('Used sentinel: ' + denseArray[i])
document.write('Size of array: ' + i)  
 

使用内置哨兵,它比 for 循环更短,并且对于大型数组可能也更有效。 sentinel 将调用者标记为发生了一些非常不寻常的事情

数组必须是dense array 才能工作!这意味着没有其他 undefined 除了数组中最后一个元素之后的值。我几乎从不使用稀疏数组,只使用密集数组,所以这对我来说没问题。

要记住的另一个更重要的点(感谢@Jack Bashford 提醒)是这不仅仅是未定义为哨兵。如果数组值为 0、假或任何其他假值,则循环将停止。所以,你必须确保数组中的数据没有falsy values,即0""''、``、nullundefinedNaN

这里是否存在“超出范围”问题,或者我们可以将 Javascript 中的数组视为“无限”,只要内存未满? 未定义是否意味着浏览器可以将其设置为任何值,因为它是未定义的,还是我们可以认为条件测试总是有效的? Javascript 中的数组很奇怪,因为“它们是对象”所以问得更好。

我无法使用以下标签在 Stackoverflow 上找到答案:[javascript] [sentinel] [while-loop] [arrays]结果为零

我已经考虑了一段时间,并用它来开始担心。但我想使用它,因为它优雅、易于查看、简短,并且可能在大数据中有效。 i 是数组的大小很有用。

更新

  • @Barmar 告诉:JS 保证一个未初始化的数组 元素将返回未定义的值。
  • MDN confirms: 使用 无效的索引号返回未定义。
  • @schu34 的注释:最好使用denseArray.forEach((x)=&gt;{...code}) 对其使用进行优化并为开发人员所知。无需遇到虚假值。它有good browser support

【问题讨论】:

  • 嗯,这不是只是 undefined 作为哨兵。如果数组值为0false 或任何其他虚假值,则循环将停止。
  • 好点!我必须编辑:)
  • 这会起作用,但它是单调的,所以其他程序员会觉得它令人困惑。我看不出它比for 循环或使用forEach() 方法“更有效”。
  • JS保证未初始化的数组元素返回值undefined
  • 值得注意的是,大多数 JS 开发人员倾向于使用高阶函数来迭代数组。类似denseArray.forEach((x)=&gt;{...code})

标签: javascript arrays while-loop sentinel


【解决方案1】:

即使您的代码以后不会被其他人查看,最好让它尽可能具有可读性和条理。条件测试中的赋值(除了递增和递减运算符)通常是个坏主意。

您的检查也需要更具体一些,因为[0, ''] 都评估为假。

denseArray = [1,2,3,4,5, '...', 99999]

for(let i = 0; i < denseArray.length; i++) {
  let x = denseArray[i]
  document.write(x + '<br>');
  console.log(x);
  if (/* check bad value */) break;
}

document.write('Used sentinel: ' + denseArray[i])
document.write('Size of array: ' + i)  

根据我的经验,如果以可读性甚至可靠性为代价,那么节省几行代码通常是不值得的。

编辑:这是我用来测试速度的代码

const arr = [];
let i;

for (i = 0; i < 30000000; i++) arr.push(i.toString());

let x;

let start = new Date();

for(i = 0; i < arr.length; i++) {
  x = arr[i];
  if (typeof x !== 'string') break;
}

console.log('A');
console.log(new Date().getTime() - start.getTime());

start = new Date();

i = 0;
while (x = arr[i++]) {
}

console.log('B');
console.log(new Date().getTime() -start.getTime());

start = new Date();

for(i = 0; i < arr.length; i++) {
  x = arr[i];
  if (typeof x !== 'string') break;
}

console.log('A');
console.log(new Date().getTime() - start.getTime());

start = new Date();

i = 0;
while (x = arr[i++]) {
}

console.log('B');
console.log(new Date().getTime() -start.getTime());


start = new Date();

for(i = 0; i < arr.length; i++) {
  x = arr[i];
  if (typeof x !== 'string') break;
}

console.log('A');
console.log(new Date().getTime() - start.getTime());

start = new Date();

i = 0;
while (x = arr[i++]) {
}

console.log('B');
console.log(new Date().getTime() -start.getTime());

for 循环甚至有一个额外的 if 语句来检查错误值,而且速度仍然更快。

【讨论】:

  • 你错过了作业,所以我编辑了帖子,把它放在关闭的第一行。它也可以在括号内。
  • 比较 for (let i = 0, x; i &lt; denseArray.length; x = denseArray[i]; i++)let i, x; while (x = denseArray[i++])。如果数据没有错误值,它们是相当相等的。更多地了解大数据可能会通过智能代码调整来提高效率。跳过i &lt; denseArray.length 测试也许会更快?
  • @PauliSudarshanTerho 不,它不会变得更快。当您的x = denseArray[i] 可以访问越界索引时,因为i 不受数组长度的限制,js 引擎将需要在访问属性时隐式进行边界检查。这非常缓慢。 JS 引擎针对惯用代码而非短代码进行了优化。
  • 我将其添加到我的答案中
  • 我还在我的自我回答中添加了一个 jsperf 测试,以确认您的测试 - forwhile 稍快。不错的测试 - 我会使用 performance.now()。 +1 得出与我相同的结论。
【解决方案2】:

搜索javascript assignment in while得到结果:

意见从it looks like a common error where you try to compare valuesIf there is quirkiness in all of this, it's the for statement's wholesale divergence from the language's normal syntax 不等。 for 是增加冗余的语法糖。它还没有与if-goto 一起过时while

首先的问题是它是否安全。 MDN 说:Using an invalid index number returns undefined in Array,所以使用起来很安全。在条件下对作业进行测试是安全的。多个赋值可以在同一个中完成,但带有varletconst 的声明不会像赋值一样返回,因此声明必须在条件之外。有一个评论,以便将来向其他人或您自己解释该数组必须保持密集而没有虚假值,否则它可能会出错

要允许false0""(除undefined 之外的任何假),然后将其扩展为:while ((x = denseArray[i++]) !== undefined) ...,但它并不比普通的数组长度比较好。

有用吗? Yes:

while( var = GetNext() )
{
  ...do something with var 
}

否则必须写出来

var = GetNext();
while( var )
{
 ...do something
 var = GetNext();
}

一般来说,最好使用开发人员熟知的denseArray.forEach((x) =&gt; { ... })。无需考虑虚假值。它有good browser support但是很慢!

I made a jsperf 显示 forEach 比 while 慢 60%!测试还显示在我的机器上forwhile 稍快!另请参阅@Albert 答案,测试表明 for 比 while 稍快

虽然while 的这种使用是安全的它可能不是没有错误的。在编码时,您可能知道您的数据,但您不知道是否有人复制粘贴代码以用于其他数据。

【讨论】:

  • 我做了一个自我回答来放置我的发现,而不是为问题添加更新。
  • 现在在我的代码中更改为 forEach!我不喜欢多余的 ')' !在几个 }}})}}}
猜你喜欢
  • 1970-01-01
  • 2012-10-11
  • 1970-01-01
  • 2020-08-14
  • 1970-01-01
  • 1970-01-01
  • 2016-08-31
  • 1970-01-01
  • 2020-01-29
相关资源
最近更新 更多