Javascript 数组本身在迭代时变为未定义

标签 javascript arrays

我正在 codeschool.com 上进行一些 javascript 挑战。 在函数表达式章节中,我遇到了以下奇怪的错误。

在下面的脚本中(我知道可以对其进行大量重构),“queue”变量在第二次运行后被分配了“undefined”值。

第一次迭代:

  1. 打印队列数组,包含4个元素
  2. 打印“hello 1”

第二次迭代:

  1. 打印队列数组,包含3个元素
  2. 打印“hello 3”
  3. 打印队列数组,包含2个元素

第三次迭代 - 错误(?)发生的位置:

  1. 打印未定义 - 而不是仍应包含 2 个元素的队列数组

首先我以为 codeschool 的端出了问题,但是在尝试在 chrome 的开发工具中运行代码后,我得到了相同的结果。 据我所知,不应该发生任何可能会造成困惑的异步情况?

var puzzlers = [
  function(a) { return 8 * a - 10; },
  function(a) { return (a - 3) * (a - 3) * (a - 3); },
  function(a) { return a * a + 4; },
  function(a) { return a % 5; }
];
var start = 2;

var applyAndEmpty = function(input, queue) {
  for (var i = 0; i < queue.length; i++) {
    alert(queue);
    if (i === 0) {
      alert("hello 1");
      var output = queue.shift()(input);
    } else if (queue.length === 1) {
      alert("hello 2");
      return queue.shift()(output);
    } else {
      alert("hello 3");
      output = queue.shift()(output);
      alert(queue);
    }
  }
};

alert(applyAndEmpty(start, puzzlers));

谢谢!

最佳答案

代码审查

这段代码中发生了一些不好的事情;我们应该先回顾一下它们。为了方便起见,我将用 console.log 调用替换所有 alert 调用 - 控制台更适合调试。

好的,第一个问题是您在 for 循环中使用 queue.shift() 。 Array.prototype.shift 将更改数组的长度,因此您不应该在循环内使用它(在非常特殊的情况之外)。

因此,每次循环时,i 都会增加一,queue.length 会减少一 - 这两个值彼此收敛,这意味着您实际上永远不会触摸队列中的所有值

<小时/>

重构

我们可以通过对函数进行非常简单的调整来解决这个问题 - 删除 for 循环! queue.shift() 对我们来说有效地递增,但一次删除一个元素。我们需要做的就是检查 queue.length 是否为空 - 如果是,我们就完成了,否则 从队列中转移一项并重复

var applyAndEmpty = function(input, queue) {
  console.log(input)
  if (queue.length === 0)
    return input;
  else
    return applyAndEmpty(queue.shift()(input), queue)
}

var puzzlers = [
  function(a) { return 8 * a - 10; },
  function(a) { return (a - 3) * (a - 3) * (a - 3); },
  function(a) { return a * a + 4; },
  function(a) { return a % 5; }
];
var start = 2;

console.log(applyAndEmpty(start, puzzlers));
// [initial input]               2
// 8 * 2 - 10                  = 6
// (6 - 3) * (6 - 3) * (6 - 3) = 27
// 27 * 27 + 4                 = 733
// 733 % 5                     = 3
// [final output]              = 3
console.log(puzzlers); // []

<小时/>

更大的教训

关于手动单步执行数组,需要了解一件重要的事情。您执行以下选项其中一个

<强>1。在尝试获取值之前检查数组是否为空

// there are no items so we cannot get one
if (arr.length === 0)
  doSomething;
// we know there is at least one item, so it is safe to get one
else
  doOtherThing( arr.shift() )

<强>2。对您尝试获取的内容进行空检查

x = queue.shift();
// we didn't get a value, queue must've been empty
if (x === undefined)
  doSomething

// yay we got something, we can use x now
else
  doOtherThing( x )

使用其中一种而不是另一种是您个人的选择,尽管我通常不喜欢这种类型的空检查。我认为最好在尝试获取数组之前检查它是否具有值。

同时执行这两个选项将是一个逻辑错误。如果我们检查数组的长度是否为非 0,我们可以推断出我们有一个 x - 因此我们不必对 x 进行空值检查。

<小时/>

备注

我不知道这是为了作业还是其他什么,但你基本上是从头开始编写函数组合的——尽管这是一种特殊的破坏性组合。

看来您已经意识到破坏性的本质,因为您已将函数命名为 applyAndEmpty,但以防万一您不知道这是必要的,我将分享一个非破坏性的下面的例子。

var lcompose = function(fs) {
  return function (x) {
    if (fs.length === 0)
      return x
    else
      return lcompose(fs.slice(1))(fs[0](x))
  }
}

var puzzlers = [
  function(a) { return 8 * a - 10; },
  function(a) { return (a - 3) * (a - 3) * (a - 3); },
  function(a) { return a * a + 4; },
  function(a) { return a % 5; }
];

var start = 2;

console.log(lcompose(puzzlers)(start)) // 3

关于Javascript 数组本身在迭代时变为未定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42493057/

相关文章:

c# - 创建一个 do while 循环,只要给出特定的用户输入就可以结束

python - 计算二维数组中跨维度的平均值

java - 如何将类中创建的所有对象存储在 Java 中该类返回的单个数组中?

javascript - 仅当键是字母或数字时才对 keyup 发出警报

javascript - 传单错误 : Object function has no method 'createIcon' when creating custom icon markers in LayerGroup

Javascript(正则表达式): How do i replace (Backslash + Double Quote) pairs?

javascript - 如何使用数据库查询为饼图添加列

java - 在java中滑动数组 - 基于算法

arrays - 使用 swift 将初始化的数组包装到另一个数组中

javascript - 从同一对象的不同实例中触发另一个实例的函数