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

时间:2017-02-27 18:21:19

标签: javascript arrays

我在codeschool.com上做了一些javascript挑战。 在功能表达式章节中,我遇到了以下奇怪的错误。

在下面的脚本中 - 可以重构一遍,我知道 - 在第二次运行后,'queue'变量被赋值为'undefined'。

第一次迭代:

  1. 打印队列数组,包含4个元素
  2. “你好1”打印
  3. 第二次迭代:

    1. 打印队列数组,包含3个元素
    2. “你好3”打印
    3. 打印队列数组,包含2个元素
    4. 第三次迭代 - 发生错误(?):

      1. 打印未定义 - 而不是仍应包含2个元素的队列数组
      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));
        

        谢谢!

3 个答案:

答案 0 :(得分:3)

代码审核

这段代码中发生了一些不好的事情;我们应该先审视它们。为方便起见,我将使用alert调用替换所有console.log个调用 - 控制台可以更好地进行调试。

好的第一个问题是您在queue.shift()循环中使用forArray.prototype.shift将更改数组的长度,因此您并不打算在循环内使用它(在非常专业的情况之外)。

因此,每次循环播放时,i上升一个,queue.length下降一个 - 两个值相互收敛,这意味着您永远不会真正触及{中的所有值{1}}

<强>重构

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

&#13;
&#13;
shift
&#13;
&#13;
&#13;

更多教训

有关手动逐步执行数组的重要事项。您执行一个以下选项

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

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); // []

<强> 2。 null-检查你想要获得的东西

// 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() )

你个人选择使用一个而不是另一个,所以我通常不喜欢这种无效检查。我相信在尝试抓住它之前检查数组是否有价值会更好。

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

<强>说明

我不知道这是用于作业还是其他什么,但你基本上是从头开始编写功能组合 - 这是一种特殊的破坏性组合。

您似乎已经意识到了破坏性,因为您已将您的功能命名为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 ) ,但为了防止您不知道这是必要的,我会这样做在下面分享一个非破坏性的例子。

&#13;
&#13;
applyAndEmpty
&#13;
&#13;
&#13;

答案 1 :(得分:1)

您获得的undefined来自此alert()来电:

alert(applyAndEmpty(start, puzzlers));

在第二次迭代结束时,i将为1,队列长度为2。因此,i在下一次迭代中递增,并且循环条件不再成立 - i为2,并且不再小于队列长度。

调试包含一些消息而不仅仅是值时,这是一个好习惯,这样你就可以告诉另一个调试输出行了。使用console.log()代替alert()也是个好主意。

答案 2 :(得分:1)

假设以下内容解决了您的问题。

  • 您在第二个条件中使用了queue.length,在该阶段,您只执行一个班次并期望4个大小的队列为2个大小,这是一个错误。

&#13;
&#13;
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++) {
    if (i === 0) {
      alert("hello 1");
      var output = queue.shift()(input);
    } else if (i === 1) {
      alert("hello 2");
      return queue.shift()(output);
    } else {
      alert("hello 3");
      output = queue.shift()(output);
    }
  }
};

alert(applyAndEmpty(start, puzzlers));
&#13;
&#13;
&#13;