【发布时间】:2017-11-23 02:15:59
【问题描述】:
我正在尝试解决一个需要修复几个错误的编程练习。最终,它应该用一个单元格网格来表示一个动画,其中在每个时间步长,如果一个单元格恰好有三个活着的邻居(每个单元格有 8 个邻居),它就会“复活”,如果它有更少超过 2 个或超过 3 个邻居,它“死”(邻居“环绕”网格)。初始脚本如下:
<html>
<head>
<script type="text/javascript">
var Board;
var xsize = 10;
var ysize = 10;
var dead = 0;
var alive = 1;
function Neighbors(Board, x, y)
{
var n = 0
for(dx=-1;dx < 1; ++dx)
for(dy=-1;dy < 1; ++dy)
{
var ax = x+dx;
var ay = y+dy;
if(Board[ax][ay]==alive) ++n;
}
return n;
}
function Kill(Board,x,y)
{
if(Board[x][y] == alive)
Board[x][y] = dead;
}
function MakeLive(Board,x,y)
{
if(Board[x][y] == dead)
Board[x][y] = alive;
}
function NextStep(Board)
{
for(var x = 0; x <= xsize; ++x)
{
for(var y = 0; y <= ysize; ++x)
{
n = Neighbors(Board,x,y);
if(n=3) MakeLive(Board,x,y);
if((n<2)||(n>3)) Kill(Board,x,y);
}
}
}
function DrawBoard(Board)
{
var Text = "";
for(var y = 0; y < ysize; ++y)
{
for(var x = 0; x < xsize; ++x)
Text += Board[x][y]==alive ? "o":"_";
Text += "<br/>";
}
document.getElementById("board").innerHTML = Text;
}
function Main()
{
// *** Change this variable to choose a different baord setup from below
var BoardSetup = "blinker";
Board = new Array(xsize);
for(var x = 0; x < xsize; ++x)
{
Board[x] = new Array(ysize);
for(var y = 0; y < ysize; ++y)
Board[x][y] = 0;
}
if(BoardSetup == "blinker")
{
Board[1][0] = 1;
Board[1][1] = 1;
Board[1][2] = 1;
}
else if(BoardSetup == "glider")
{
Board[2][0] = 1;
Board[2][1] = 1;
Board[2][2] = 1;
Board[1][2] = 1;
Board[0][1] = 1;
}
else if(BoardSetup == "flower")
{
Board[4][6] = 1;
Board[5][6] = 1;
Board[6][6] = 1;
Board[7][6] = 1;
Board[8][6] = 1;
Board[9][6] = 1;
Board[10][6] = 1;
Board[4][7] = 1;
Board[6][7] = 1;
Board[8][7] = 1;
Board[10][7] = 1;
Board[4][8] = 1;
Board[5][8] = 1;
Board[6][8] = 1;
Board[7][8] = 1;
Board[8][8] = 1;
Board[9][8] = 1;
Board[10][8] = 1;
}
DrawBoard(Board);
}
</script>
</head>
<body onload="Main()">
<div id="board">
</div>
<a href="#IGoNowhere" onclick="NextStep(Board);DrawBoard(Board);">Next -></a>
</body>
</html>
问题是,如果我按下“下一步”按钮,在控制台中我会看到以下错误:
Uncaught TypeError: Cannot read property '-1' of undefined
at Neighbors (life_broken__281_29.html:19)
at Function.NextStep (life_broken__281_29.html:42)
at HTMLAnchorElement.onclick (life_broken__281_29.html:117)
Neighbors @ life_broken__281_29.html:19
NextStep @ life_broken__281_29.html:42
onclick @ life_broken__281_29.html:117
我认为问题在于Board 是在Main() 函数中定义的,它不在onclick 回调函数的范围内。
我最初的方法是将Board 的初始化移到Main() 函数之外,使其成为全局变量,并从所有函数调用中删除Board。然而,这似乎不是一种优雅的方法。相反,我尝试使用Function.prototype.call(),如下所示:
<a href="#IGoNowhere" onclick="NextStep.call(Main, Board); DrawBoard.call(Main, Board);">Next -></a>
此外,我实现了一个wrapAround 函数来避免索引超出范围:
function Neighbors(Board, x, y)
{
var n = 0
for(dx=-1;dx < 1; ++dx)
for(dy=-1;dy < 1; ++dy)
{
var ax = x+dx;
var ay = y+dy;
ax = wrapAround(ax, xsize);
ay = wrapAround(ay, ysize);
if(Board[ax][ay]==alive) ++n;
}
return n;
}
function wrapAround(coordinate, size) {
var result = coordinate % size;
if (result < 0) {
result += size;
}
return result;
}
但是,现在我收到一个新错误:
life_broken__281_29.html:42 Uncaught TypeError: Cannot read property '0' of undefined
at MakeLive (life_broken__281_29.html:42)
at Function.NextStep (life_broken__281_29.html:53)
at HTMLAnchorElement.onclick (life_broken__281_29.html:127)
显然,Neighbors 函数现在没有引发任何错误,但 NextStep 中的下一个函数 MakeLive 是。但是我不明白,因为它们都定义在相同的“级别”并且在NextStep 中具有相似的调用。谁能解释这里的问题是什么?
更新
确实Board 是在全局范围内声明的,因此不需要Function.prototype.call()。 (我习惯了 Python 的声明和定义总是在同一个地方)。我还将布尔表达式更改为(x === 3)。
但是,由于某种原因,x 仍然会上升到 10,即使我将 <= 替换为 <。这是更新后的代码,带有console.log 调试语句:
<html>
<head>
<script type="text/javascript">
var Board;
var xsize = 10;
var ysize = 10;
var dead = 0;
var alive = 1;
function Neighbors(Board, x, y)
{
var n = 0
for(dx=-1;dx < 1; ++dx)
for(dy=-1;dy < 1; ++dy)
{
var ax = x+dx;
var ay = y+dy;
ax = wrapAround(ax, xsize);
ay = wrapAround(ay, ysize);
if(Board[ax][ay]==alive) ++n;
}
return n;
}
function wrapAround(coordinate, size) {
var result = coordinate % size;
if (result < 0) {
result += size;
}
return result;
}
function Kill(Board, x, y)
{
if (Board[x][y] == alive)
Board[x][y] = dead;
}
function MakeLive(Board, x, y)
{
if (Board[x][y] == dead)
Board[x][y] = alive;
}
function NextStep(Board)
{
for(var x = 0; x < xsize; ++x)
{
for(var y = 0; y < ysize; ++x)
{
n = Neighbors(Board,x,y);
console.log("x = " + x + ", y = " + y + ", n = " + n);
if (n===3) MakeLive(Board,x,y);
if ((n<2)||(n>3)) Kill(Board,x,y);
}
}
}
function DrawBoard(Board)
{
var Text = "";
for(var y = 0; y < ysize; ++y)
{
for(var x = 0; x < xsize; ++x)
Text += Board[x][y]==alive ? "o":"_";
Text += "<br/>";
}
document.getElementById("board").innerHTML = Text;
}
function Main()
{
// *** Change this variable to choose a different baord setup from below
var BoardSetup = "blinker";
Board = new Array(xsize);
for(var x = 0; x < xsize; ++x)
{
Board[x] = new Array(ysize);
for(var y = 0; y < ysize; ++y)
Board[x][y] = 0;
}
if(BoardSetup == "blinker")
{
Board[1][0] = 1;
Board[1][1] = 1;
Board[1][2] = 1;
}
else if(BoardSetup == "glider")
{
Board[2][0] = 1;
Board[2][1] = 1;
Board[2][2] = 1;
Board[1][2] = 1;
Board[0][1] = 1;
}
else if(BoardSetup == "flower")
{
Board[4][6] = 1;
Board[5][6] = 1;
Board[6][6] = 1;
Board[7][6] = 1;
Board[8][6] = 1;
Board[9][6] = 1;
Board[10][6] = 1;
Board[4][7] = 1;
Board[6][7] = 1;
Board[8][7] = 1;
Board[10][7] = 1;
Board[4][8] = 1;
Board[5][8] = 1;
Board[6][8] = 1;
Board[7][8] = 1;
Board[8][8] = 1;
Board[9][8] = 1;
Board[10][8] = 1;
}
DrawBoard(Board);
}
</script>
</head>
<body onload="Main()">
<div id="board">
</div>
<a href="#IGoNowhere" onclick="NextStep(Board); DrawBoard(Board);">Next -></a>
</body>
</html>
这是我单击“下一步”时控制台的结果:
x = 0, y = 0, n = 0
life_broken__281_29.html:53 x = 1, y = 0, n = 1
life_broken__281_29.html:53 x = 2, y = 0, n = 0
life_broken__281_29.html:53 x = 3, y = 0, n = 0
life_broken__281_29.html:53 x = 4, y = 0, n = 0
life_broken__281_29.html:53 x = 5, y = 0, n = 0
life_broken__281_29.html:53 x = 6, y = 0, n = 0
life_broken__281_29.html:53 x = 7, y = 0, n = 0
life_broken__281_29.html:53 x = 8, y = 0, n = 0
life_broken__281_29.html:53 x = 9, y = 0, n = 0
life_broken__281_29.html:53 x = 10, y = 0, n = 0
life_broken__281_29.html:36 Uncaught TypeError: Cannot read property '0' of undefined
at Kill (life_broken__281_29.html:36)
at NextStep (life_broken__281_29.html:55)
at HTMLAnchorElement.onclick (life_broken__281_29.html:128)
我有点不明白为什么会发生这种情况,因为这种方式的简单 for 循环确实有效:
for (var i = 0; i < 10; ++i) {
console.log("i = " + i);
}
VM158:2 i = 0
VM158:2 i = 1
VM158:2 i = 2
VM158:2 i = 3
VM158:2 i = 4
VM158:2 i = 5
VM158:2 i = 6
VM158:2 i = 7
VM158:2 i = 8
VM158:2 i = 9
undefined
控制台是否以某种方式使用旧代码的缓存版本? (我正在使用括号中的实时预览)。
更新 2
这是因为我应该使用后增量而不是前增量(参见http://jsforallof.us/2014/07/10/pre-increment-vs-post-increment/)。将++x 更改为x++ 解决了这个问题。
【问题讨论】:
-
Board是一个全局变量。Main()分配给它,它没有在本地声明它。
标签: javascript html scope