首页 > 解决方案 > 为什么在 JavaScript for Loop 中声明 vs 不声明变量的行为不同?

问题描述

编辑:将标题更改为更相关的问题。

我发现 JavaScript 有一个错误。如果您在没有变量声明的情况下设置变量letvar. 例如for (n=1;n<10;n++)vs 变量声明for (let n=1;n<10;n++)。您可以n在函数外部调用变量,但它违反了 for-loop 条件n<10。输出是15

JavaScript for Loop 没有变量声明。for (n=1;n<10;n++)

grid = [
  [5, 3, 0, 0, 7, 0, 0, 0, 0],
  [6, 0, 0, 1, 9, 5, 0, 0, 0],
  [0, 9, 8, 0, 0, 0, 0, 6, 0],
  [8, 0, 0, 0, 6, 0, 0, 0, 3],
  [4, 0, 0, 8, 0, 3, 0, 0, 1],
  [7, 0, 0, 0, 2, 0, 0, 0, 6],
  [0, 6, 0, 0, 0, 0, 2, 8, 0],
  [0, 0, 0, 4, 1, 9, 0, 0, 5],
  [0, 0, 0, 0, 8, 0, 0, 7, 9]
];

function possible(r, c, n) {
  /// check the row
  for (let i = 0; i < 9; i++)
    if (grid[r][i] == n) return false;
  /// check the column
  for (let i = 0; i < 9; i++)
    if (grid[i][c] == n) return false;
  /// check the 3x3 grid
  r0 = Math.floor(r / 3) * 3;
  c0 = Math.floor(c / 3) * 3;
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (grid[r0 + i][c0 + j] == n) return false;
    }
  }
  /// all check passed
  return true;
}

function solve() {
  for (let r = 0; r < 9; r++) {
    for (let c = 0; c < 9; c++) {
      /// check grid with value of 0
      if (grid[r][c] === 0) {
        /// check for possible solution
        for (n = 1; n < 10; n++) {
          if (possible(r, c, n)) {
            /// there is a possibility of the selected solution is a bad one.
            /// to solve this, use backtracking: try -> if it turns out the solution is a bad one, we go back to 0.
            grid[r][c] = n;
            /// recursion
            solve();
            grid[r][c] = 0;
          }
        }
        /// if there is no solution, we have to return.
        return;
      }
    }
  }
  console.log(grid);
  prompt("More?")
}

solve();

console.log(n)

带有变量声明的 JavaScript for Loopfor (let n=1;n<10;n++)

grid = [
  [5, 3, 0, 0, 7, 0, 0, 0, 0],
  [6, 0, 0, 1, 9, 5, 0, 0, 0],
  [0, 9, 8, 0, 0, 0, 0, 6, 0],
  [8, 0, 0, 0, 6, 0, 0, 0, 3],
  [4, 0, 0, 8, 0, 3, 0, 0, 1],
  [7, 0, 0, 0, 2, 0, 0, 0, 6],
  [0, 6, 0, 0, 0, 0, 2, 8, 0],
  [0, 0, 0, 4, 1, 9, 0, 0, 5],
  [0, 0, 0, 0, 8, 0, 0, 7, 9]
];

function possible(r, c, n) {
  /// check the row
  for (let i = 0; i < 9; i++)
    if (grid[r][i] == n) return false;
  /// check the column
  for (let i = 0; i < 9; i++)
    if (grid[i][c] == n) return false;
  /// check the 3x3 grid
  r0 = Math.floor(r / 3) * 3;
  c0 = Math.floor(c / 3) * 3;
  for (let i = 0; i < 3; i++) {
    for (let j = 0; j < 3; j++) {
      if (grid[r0 + i][c0 + j] == n) return false;
    }
  }
  /// all check passed
  return true;
}

function solve() {
  for (let r = 0; r < 9; r++) {
    for (let c = 0; c < 9; c++) {
      /// check grid with value of 0
      if (grid[r][c] === 0) {
        /// check for possible solution
        for (let n = 1; n < 10; n++) {
          if (possible(r, c, n)) {
            /// there is a possibility of the selected solution is a bad one.
            /// to solve this, use backtracking: try -> if it turns out the solution is a bad one, we go back to 0.
            grid[r][c] = n;
            /// recursion
            solve();
            grid[r][c] = 0;
          }
        }
        /// if there is no solution, we have to return.
        return;
      }
    }
  }
  console.log(grid);
  prompt("More?")
}

solve();

console.log(n)

标签: javascriptpythonrecursion

解决方案


为了更明确一点,在solve两个片段中的函数内部,嵌套在两个for循环和一个if语句中,我们有这两个块中的任何一个——唯一的区别是第一个不声明变量n,第二个一个做:

        for (n = 1; n < 10; n++) {
          if (possible(r, c, n)) {
            grid[r][c] = n;
            solve();
            grid[r][c] = 0;
          }
        }
        for (let n = 1; n < 10; n++) {
          if (possible(r, c, n)) {
            grid[r][c] = n;
            solve();
            grid[r][c] = 0;
          }
        }

let和之间的区别在这里没有问题var。如果你使用第二个仍然可以工作var,尽管你必须小心不要在没有重新初始化的情况下在函数中重用它——这不是问题let,变量的作用域只限于for声明它的 -loop .

但是 JS 中声明的变量和未声明的变量之间存在严重差异。声明的变量仅限于定义它的范围;let并且const对于包含块和var包含函数都是本地的。一个未声明的变量在整个程序中都是全局的;如果任何地方发生任何变化n,它就会因您的功能而改变。

因此,如果我们首先检查正确的第二个样本,我们声明nfor-loop 的本地,尝试存在 的可能性n1并且说,发现它不可能,所以继续前进2,这也是不可能的,但是当我们试一下3,我们发现是可以的。(我没有使用您的示例网格,只是在这里制作内容;无论您的数据如何,这个想法都应该是相同的。)所以现在我们递归调用solve并再次到达这个循环。我们创建另一个作用域变量 n,用它做我们的工作我们完成了那个调用并返回到这个循环,我们的初始 n值在范围内,在下一次迭代中,将它设置为4. 我们继续我们的快乐方式。(这里看起来还有其他工作要做,以使其成为一个完整的数独求解器;在快速分析中,您总是回溯并且从未实际报告已解决的网格;但这与变量声明/范围的讨论无关。)

现在我们尝试您的第一个样品。我们不声明n,所以它被设置为一个全局变量。我们尝试存在 和 的可能性n12是不可能的。我们尝试3,这是可能的,所以我们递归调用solve并再次到达这个循环。我们初始化n为 1,并遍历所有的可能性,n在循环结束后结束,意思n是 10。我们从递归调用返回到我们的第一个循环,到达 line grid[r][c] = 0,然后我们到达我们的增量步骤for循环,n++,我们将(全局,记住)值设置n11。由于n < 10is false,我们完成了循环和函数。

当您登录时n,您现在将获得11,一路上跳过了大部分可能性。(在您的数据中,您得到15,这意味着您在退出之前会退出更多级别的递归;同样,我正在发明数据以显示问题的想法,而不是您的数据的详细信息。)

教训应该很简单:总是声明你的变量。这些天来,没有真正的理由使用var. 我的建议是尽可能多地使用,当你不能const使用时使用。let但这不是重要的部分。重要的部分就是永远不要使用未声明的变量。


推荐阅读