【问题标题】:Adjusting the scope of the callback function of onclick调整onclick的回调函数范围
【发布时间】: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,即使我将 &lt;= 替换为 &lt;。这是更新后的代码,带有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


【解决方案1】:

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

您最初的问题是因为您在x = 0dx = -1 时访问的是Board 数组外部,而您使用wrapAround() 函数解决了这个问题。

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

【讨论】:

    【解决方案2】:

    if(n=3) MakeLive(Board,x,y);,您的n = 3 应该是n === 3,我确定您不想将3 分配给n,这会导致truthy 值,这将调用MakeLive(Board,x,y);每次。

    此外,在 NextStep 中,您的 xy 一直到 xsizeysize (&lt;=) 而在其他任何地方使用 &lt;,认为这会导致undefinedBoard[x] 中的值

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-09
      • 2010-11-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多