我无法理解为什么这段代码表现得像:
for (var i = 1; i < 3; i++) {
var j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's " + j);
}
}
(jsFiddle)
如果我将j
设置为null
并检查null
,则会按照我认为的方式运作。
这不是Java,C#,C ++等的工作方式,因而是混乱。
答案 0 :(得分:10)
这是因为JavaScript中的变量范围限定为函数(如果不在函数内,则为全局范围)。 var
不在循环内部,并且花括号不定义新的闭包。基本上,j
的值在循环体后面仍然存在,var
不会将j
重新定义为undefined
。这就是明确设置var j = null;
具有预期效果的原因。
考虑到这一点的一个好方法是,只要你用var
声明变量,就像这样。
function someFunc() {
for(var i = 0; i < 3; i++){
var j;
}
}
解释器提升变量声明,如此。
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
}
}
请注意,由于var j
声明被提升到函数的顶部,声明实际上在循环中什么也没做。
但是,如果您要使用null
这样初始化变量。
function someFunc() {
for(var i = 0; i < 3; i++){
var j = null;
}
}
它将被解释为这样。
function someFunc() {
var i;
var j;
for(i = 0; i < 3; i++){
j = null;
}
}
注意每个循环如何j
设置为null
。
ES6中有一个关键字会在这样的循环中创建一个范围,它是let
关键字。请注意,此时let
关键字的浏览器支持效果不佳。
for (var i = 1; i < 3; i++) {
let j;
if (!j) {
j = 1;
} else {
alert("why are we here? j shouldn't be defined but it's "+ j);
}
}
答案 1 :(得分:4)
第一次for
循环执行循环体时,j
未定义,因此您的代码设置为j=1
。在循环的后续迭代中,j
已经定义并设置为1
,因此它会按照预期进入else
子句。
这是因为在Javascript中使用var
定义的变量是函数作用域,而不是块作用域,如果不在函数内,则它们是全局的。因此,jsFiddle中只有一个变量j
,for
循环的每次迭代都使用相同的变量(从而继承上一次迭代的值)。
如果您在j = null;
循环体内初始化for
,它将起作用,因为那时您将为每次迭代重新初始化它,而不是使用上一次迭代中的值。
ES6建议添加范围为最近的块的let
声明。有关let
的详细信息,请参阅What's the difference between using "let" and "var" to declare a variable?。
答案 2 :(得分:2)
JavaScript中没有块范围,只有global and function scopes。虽然,JavaScript variable statements非常灵活,它们会给程序员一个块范围可能存在的假象,但事实是JavaScript函数中声明的变量后来由解释器hoisted。这意味着它们的声明被移动到最近声明的函数的顶部。以此为例:
function test() {
console.log('a test');
for (var i = 0; i < 100; i++) {
var k = i + Math.random();
console.log(k)
}
}
JavaScript解释器在内部将代码“转换”为以下内容:
function test() {
var i, k; // var declarations are now here!
console.log('a test');
for (i = 0; i < 100; i++) {
k = i + Math.random();
console.log(k)
}
}
它会将所有var
声明移动到最近的函数声明的开头。在您的代码中,提升将创建以下代码:
// A anonymous function created by jsFiddle to run your script
function () {
var i, j;
for (i = 1; i < 3; i++) {
if (!j) {
j = 1;
} else {
alert("Why are we here? j shouldn't be defined, but it's " + j);
}
}
}
第一次变量为undefined
,然后分配1
,然后打印出您的信息。