JavaScript 中未初始化变量的范围
Scope of uninitialized variable in JavaScript
我不明白为什么这段代码的行为是这样的:
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++ 等的工作方式,因此会造成混淆。
这是因为 JavaScript 中的变量作用域为函数(如果不在函数内,则为全局作用域)。 var
不在循环内,花括号不定义新的闭包。基本上,j
的值在循环体之后仍然存在,而 var
不会将 j
重新定义为 undefined
。这就是显式设置 var j = null;
具有预期效果的原因。
示例 1:
考虑这一点的一个好方法是,任何时候你用 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
声明被提升到函数的顶部,该声明实际上在循环中什么也不做。
示例 2:
但是,如果您要像这样用 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 关键字:
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);
}
}
您的 for
循环第一次执行循环体时,j
未定义,因此您的代码集 j=1
。在循环的后续迭代中,j
已经定义并设置为 1
,因此它会按预期进入您的 else
子句。
这是因为在 Javascript 中用 var
定义的变量是函数范围的,而不是块范围的,如果不在函数内部,那么它们是全局的。因此,在您的 jsFiddle 中只有一个变量 j
并且 for
循环的每次迭代都使用相同的变量(因此继承了上一次迭代的值)。
如果您在 for
循环体内初始化 j = null;
它将起作用,因为这样您会为每次迭代重新初始化它,而不是使用上一次迭代的值。
ES6 建议添加 let
声明,该声明的作用域是最近的块。有关 let
.
的更多信息,请参阅 What's the difference between using "let" and "var" to declare a variable?
JavaScript 中没有块范围,只有 global and function scopes. Although, JavaScript variable statements are so flexible that they will give the programmer the illusion that a block scope could exist, but the truth is that variables declared in JavaScript functions are later hoisted 解释器。这意味着它们的声明被移动到最近声明的函数的顶部。以此为例:
function test() {
console.log('a test');
for (var i = 0; i < 100; i++) {
var k = i + Math.random();
console.log(k)
}
}
JavaScript 解释器在内部将 "transform" 代码变为以下内容:
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
,然后你的消息被打印出来。
我不明白为什么这段代码的行为是这样的:
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++ 等的工作方式,因此会造成混淆。
这是因为 JavaScript 中的变量作用域为函数(如果不在函数内,则为全局作用域)。 var
不在循环内,花括号不定义新的闭包。基本上,j
的值在循环体之后仍然存在,而 var
不会将 j
重新定义为 undefined
。这就是显式设置 var j = null;
具有预期效果的原因。
示例 1:
考虑这一点的一个好方法是,任何时候你用 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
声明被提升到函数的顶部,该声明实际上在循环中什么也不做。
示例 2:
但是,如果您要像这样用 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 关键字:
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);
}
}
您的 for
循环第一次执行循环体时,j
未定义,因此您的代码集 j=1
。在循环的后续迭代中,j
已经定义并设置为 1
,因此它会按预期进入您的 else
子句。
这是因为在 Javascript 中用 var
定义的变量是函数范围的,而不是块范围的,如果不在函数内部,那么它们是全局的。因此,在您的 jsFiddle 中只有一个变量 j
并且 for
循环的每次迭代都使用相同的变量(因此继承了上一次迭代的值)。
如果您在 for
循环体内初始化 j = null;
它将起作用,因为这样您会为每次迭代重新初始化它,而不是使用上一次迭代的值。
ES6 建议添加 let
声明,该声明的作用域是最近的块。有关 let
.
JavaScript 中没有块范围,只有 global and function scopes. Although, JavaScript variable statements are so flexible that they will give the programmer the illusion that a block scope could exist, but the truth is that variables declared in JavaScript functions are later hoisted 解释器。这意味着它们的声明被移动到最近声明的函数的顶部。以此为例:
function test() {
console.log('a test');
for (var i = 0; i < 100; i++) {
var k = i + Math.random();
console.log(k)
}
}
JavaScript 解释器在内部将 "transform" 代码变为以下内容:
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
,然后你的消息被打印出来。