我正在 codeschool.com 上进行一些 javascript 挑战。 在函数表达式章节中,我遇到了以下奇怪的错误。
在下面的脚本中(我知道可以对其进行大量重构),“queue”变量在第二次运行后被分配了“undefined”值。
第一次迭代:
- 打印队列数组,包含4个元素
- 打印“hello 1”
第二次迭代:
- 打印队列数组,包含3个元素
- 打印“hello 3”
- 打印队列数组,包含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));
谢谢!
最佳答案
代码审查
这段代码中发生了一些不好的事情;我们应该先回顾一下它们。为了方便起见,我将用 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/