在同一作用域中声明两次Javascript变量 - 是否有问题?

40

以下代码会引起任何问题吗?:

var a = 1;
var a = 2;

我的理解是JavaScript 变量在作用域开始时声明。例如:

var foo = 'a';
foo = 'b';
var bar = 'c';

被处理为:

var foo;
var bar;
foo = 'a';
foo = 'b';
bar = 'c';

因此,我的初始代码片段将变为:

var a;
a = 1;
a = 2;

或者会变成:

var a;
var a;
a = 1;
a = 2;

我知道在同一作用域内两次声明JavaScript变量并不是好的做法,但我更关心这样做的影响。


2
var a,a; 和 var a; 是一样的。如果你想使用相同的变量名但有多个值,那么你需要研究一下如何使用数组。 - user2417483
请查看有关变量作用域的这个问题 - djluis
@Curt 对不起,我盲目评论了。 - Deepak Ingole
就影响而言,我认为这是一个实现特定的问题。JS引擎不会更改源代码。但是它们可以以不同的方式处理它,应用一些优化,直到它不与ECMAScript规范冲突。规范说它是直接相同的变量。因此,我认为没有任何影响。 - likern
5个回答

39

就像你所说的那样,如果有两个或更多相同变量,在JavaScript中会将该声明移动到作用域的顶部,然后您的代码将变成这样:

var a;
a = 1;
a = 2;

因此,它不会给我们任何错误。

同样的行为也发生在for循环中(它们没有局部作用域),所以下面的代码非常常见:

for (var i = 0; i < n; i++) {
    // ...
}
for (var i = 0; i < m; i++) {
    // ...
}

这就是为什么像道格拉斯·克罗克福德这样的JavaScript大师建议程序员手动将这些声明移到作用域的顶部:

var i;    // declare here
for (i = 0; i < n; i++) {    // initialize here...
    // ...
}
for (i = 0; i < m; i++) {    // ... and here
    // ...
}

@Michel 谢谢,很高兴能帮忙。 - Danilo Valente
1
@k.stm 你最终会得到更干净的代码。你也可以避免重新声明变量。 - Danilo Valente
2
@k.stm 你还可以使用更少的字符来减少文件大小,这在向客户端发送大量js文件时非常有用:) - tkellehe
2
反方观点-如果两个变量声明除了增加一些字节外没有任何危害,那么为什么要冒险将声明移到顶部,并引入意外创建全局变量的潜在可能性,因为我认为它已经被声明了。在复杂的多文件环境中,这似乎是一种可能性。 - kpg

4
声明同一变量两次与声明一次相同,没有任何影响。下面的语句不会产生任何影响。
var a, a;

在下面的情况下,您只是覆盖变量foo。如果您在本地和全局范围内定义了foo,则会产生影响。JS首先会在本地范围内搜索foo,然后如果找不到,就会在全局范围内查找。
var foo;
var bar;
foo = 'a';
foo = 'b';
bar = 'c';

3

让我们尝试转录ECMAScript® 2021 Language Specification的内容。

首先,根据JavaScript: Understanding the Weird Parts的说法,运行代码有两个阶段:创建阶段执行阶段。从概念上讲,代码被处理两次(而不是执行)。

对于var a = 5;语句,会发生以下情况:

  • 创建阶段:为标识符a分配内存,并将未定义的值设置为该内存(称为提升
  • 执行阶段:将值5放入与标识符a相关联的内存中(而不是未定义

现在,ECMAScript® 2021 Language Specification14.3.2变量语句部分说:

一个 var 语句声明的变量是作用域限定在当前执行上下文的 VariableEnvironment 中的。当它们所属的 Environment Record 实例化时,var 变量被创建并在创建时初始化为 undefined。在任何 VariableEnvironment 的范围内,一个常见的 BindingIdentifier 可以出现在多个 VariableDeclaration 中,但这些声明只定义一个变量。由具有 InitializerVariableDeclaration 定义的变量在执行 VariableDeclaration 时被分配其 Initializer 的 AssignmentExpression 的值,而不是在创建变量时分配。

让我们逐行分析。

在任何 VariableEnvironment 的范围内,一个常见的 BindingIdentifier 可以出现在多个 VariableDeclaration 中。

在这种情况下,BindingIdentifier 只是标识符。语句表示这些语法是正式允许的。

var a;
var a;

var a = 1;
var a = 2;

var a, a;

var a = 1, a = 2;

但是,这些声明只定义了一个变量。
直接说它是同一个变量,只分配了一个内存。
从概念上讲,如果在创建阶段具有标识符(在我们的情况下为a)的变量已经被创建,则其创建将被跳过(先前的语句已将内存分配并将未定义设置为其中)。
使用初始化程序定义的变量通过其初始化程序的赋值表达式在执行VariableDeclaration时被赋值,而不是在创建变量时被赋值。
这只是进一步强调了该语句的赋值(发生在执行阶段)和变量创建(发生在创建阶段)之间的差异。
var a = 5;

说明

现在让我们总结一下我们所知道的内容。 该语法是官方支持的。这意味着只会创建一个变量(一个内存位置来存储值)

var a = 1;
var a = 2;

它与提升无关。提升仍然像往常一样发生。
console.log(a) // this is undefined
var a = 1;
var a = 2;

任务将在执行阶段逐行进行,更改位于同一内存位置的值。

console.log(a) // undefined
var a = 1;
console.log(a) // 1
var a = 2;
console.log(a) // 2

0

由于 JavaScript 的变量提升特性,这样的重复变量声明不会造成任何问题。因此,在您的 DOM 中,无论您声明了变量但是赋值(或者没有赋值),它们都将在编译期间被放置在顶部。

Example:
    var foo='hai!';
    bar='welcome!';
    var bar;

你可能期望上述代码片段会抛出“未定义”的错误,但是 JavaScript 通过将 bar 变量的声明放在顶部来管理它。因此,即使在变量声明之前,它的值也可以被赋值,与其他语言不同。

希望这能在一定程度上帮助你。


0
如果您两次初始化同一变量,控制台上不会出现错误,但如果您正在处理某些应用程序(例如猜数字游戏)并希望将所有元素重置为初始状态,则所有元素都将设置为初始状态,但是已经初始化两次的变量仅保留其修改后的值。 例如, 如果初始分数为20,然后修改为某个数字15,现在按下重置按钮以将值设置为初始值,但它将不会被重置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接