调整onclick

时间:2017-11-23 02:15:59

标签: javascript html scope

我正在尝试解决一个编程练习,其中有几个错误需要修复。最终,它应该代表一个带有细胞网格的动画,在每个时间步,如果一个细胞正好有三个活着的邻居(每个细胞有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

我认为,问题是BoardMain()函数中定义,这不在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函数现在没有引发任何错误,但NextStepMakeLive中的下一个函数是。然而,我不明白,因为它们都在相同的“级别”中定义,并且在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

控制台是否以某种方式使用旧代码的缓存版本? (我正在使用Brackets中的实时预览)。

更新2

这是因为我应该使用后增量而不是预增量(参见http://jsforallof.us/2014/07/10/pre-increment-vs-post-increment/)。将++x更改为x++解决了问题。

2 个答案:

答案 0 :(得分:1)

错误与变量范围无关。 Board是一个全局变量,因此任何函数都可以访问它。

您的原始问题是因为您在Boardx = 0时访问了dx = -1数组,并使用wrapAround()函数修复了此问题。

下一个问题是NextStep中的循环走得太远了。行索引从0变为xsize-1,列从0变为ysize-1。但是那里的循环使用x <= xsizey <= ysize,因此它会尝试访问不存在的Board[xsize]。将<=更改为<,就像Main()中的循环一样。

答案 1 :(得分:1)

if(n=3) MakeLive(Board,x,y);,您的n = 3应为n === 3,我确定您不想将3分配给n会产生truthy值,每次都会调用MakeLive(Board,x,y);

此外,在NextStep中,您的xy一直向xsizeysize<=)发送,而您使用<的其他任何地方,认为这导致undefined

中的Board[x]