javascript - 为什么在 JavaScript for Loop 中声明 vs 不声明变量的行为不同?
问题描述
编辑:将标题更改为更相关的问题。
我发现 JavaScript 有一个错误。如果您在没有变量声明的情况下设置变量let
或var
. 例如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)
解决方案
为了更明确一点,在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
,它就会因您的功能而改变。
因此,如果我们首先检查正确的第二个样本,我们声明n
为for
-loop 的本地,尝试存在 的可能性n
,1
并且说,发现它不可能,所以继续前进2
,这也是不可能的,但是当我们试一下3
,我们发现是可以的。(我没有使用您的示例网格,只是在这里制作内容;无论您的数据如何,这个想法都应该是相同的。)所以现在我们递归调用solve
并再次到达这个循环。我们创建另一个作用域变量 n
,用它做我们的工作我们完成了那个调用并返回到这个循环,我们的初始 n
值在范围内,在下一次迭代中,将它设置为4
. 我们继续我们的快乐方式。(这里看起来还有其他工作要做,以使其成为一个完整的数独求解器;在快速分析中,您总是回溯并且从未实际报告已解决的网格;但这与变量声明/范围的讨论无关。)
现在我们尝试您的第一个样品。我们不声明n
,所以它被设置为一个全局变量。我们尝试存在 和 的可能性n
,1
这2
是不可能的。我们尝试3
,这是可能的,所以我们递归调用solve
并再次到达这个循环。我们初始化n
为 1,并遍历所有的可能性,n
在循环结束后结束,意思n
是 10。我们从递归调用返回到我们的第一个循环,到达 line grid[r][c] = 0
,然后我们到达我们的增量步骤for
循环,n++
,我们将(全局,记住)值设置n
为11
。由于n < 10
is false
,我们完成了循环和函数。
当您登录时n
,您现在将获得11
,一路上跳过了大部分可能性。(在您的数据中,您得到15
,这意味着您在退出之前会退出更多级别的递归;同样,我正在发明数据以显示问题的想法,而不是您的数据的详细信息。)
教训应该很简单:总是声明你的变量。这些天来,没有真正的理由使用var
. 我的建议是尽可能多地使用,当你不能const
使用时使用。let
但这不是重要的部分。重要的部分就是永远不要使用未声明的变量。
推荐阅读
- spring - API 调用失败:HHH000206: hibernate.properties not found 更改 Spring Boot 的数据源时出现异常
- angularjs - django-rest-social-auth 中的“代码”参数是什么?
- python-3.x - 我可以在 xlsxwriter 中使用列标题,而不是在格式化时使用列字母
- azure - zip部署的Web应用程序部署失败?
- ruby - 拆分字符串错过了用于拆分它的单词
- javascript - $(window).scrollTop(value) 在移动设备上不起作用
- typescript - 创建一个从方法中提取返回值的泛型类型
- java - 如何使用列表视图调用对话框片段并从片段中搜索过滤器并将父片段中的选定项目值保存为字符串?
- excel - 如何从 Uipath 中的多个 excel 工作簿中读取数据
- python - 如何修复“+:'dict'和'int'不支持的操作数类型”