var
var
语句声明函数作用域或全局作用域变量,可以选择性地将每个变量初始化为一个值。
试一试
语法
描述
使用 var
声明的变量的作用域是以下最靠近 var
语句的带花括号的语法之一
- 函数体
- 静态初始化块
或者,如果以上都不适用
- 对于在模块模式下运行的代码,当前 模块
- 对于在脚本模式下运行的代码,全局作用域。
function foo() {
var x = 1;
function bar() {
var y = 2;
console.log(x); // 1 (function `bar` closes over `x`)
console.log(y); // 2 (`y` is in scope)
}
bar();
console.log(x); // 1 (`x` is in scope)
console.log(y); // ReferenceError, `y` is scoped to `bar`
}
foo();
重要的是,其他块构造,包括 块语句、try...catch
、switch
、一个 for
语句 的标题,不会为 var
创建作用域,并且在这样的块内使用 var
声明的变量可以在块外继续引用。
for (var a of [1, 2, 3]);
console.log(a); // 3
在脚本中,使用 var
声明的变量被添加为全局对象的不可配置属性。这意味着它的属性描述符不能更改,也不能使用 delete
删除。JavaScript 具有自动内存管理,因此能够对全局变量使用 delete
操作符毫无意义。
"use strict";
var x = 1;
Object.hasOwn(globalThis, "x"); // true
delete globalThis.x; // TypeError in strict mode. Fails silently otherwise.
delete x; // SyntaxError in strict mode. Fails silently otherwise.
在 NodeJS CommonJS 模块和原生 ECMAScript 模块 中,顶层变量声明的作用域为模块,不会作为属性添加到全局对象。
var
关键字后面的列表称为绑定列表,并用逗号分隔,其中逗号不是 逗号运算符,=
符号不是 赋值运算符。后面变量的初始化器可以引用列表中前面的变量并获取初始化的值。
提升
var
声明无论在脚本中的哪个位置出现,都会在脚本中的任何代码执行之前被处理。在代码中的任何位置声明变量都等同于在顶部声明它。这也意味着变量可以在声明之前被使用。这种行为称为 提升,因为看起来变量声明被移动到了它所在的函数、静态初始化块或脚本源的顶部。
注意:var
声明只提升到当前脚本的顶部。如果您在一个 HTML 中有两个 <script>
元素,第一个脚本在第二个脚本被处理和执行之前无法访问第二个脚本声明的变量。
bla = 2;
var bla;
这被隐式理解为
var bla;
bla = 2;
因此,建议始终在它们作用域的顶部(全局代码的顶部和函数代码的顶部)声明变量,以便清楚地了解哪些变量的作用域是当前函数。
只有变量的声明被提升,而不是它的初始化。初始化只在到达赋值语句时发生。在此之前,变量仍然是 undefined
(但已声明)
function doSomething() {
console.log(bar); // undefined
var bar = 111;
console.log(bar); // 111
}
这被隐式理解为
function doSomething() {
var bar;
console.log(bar); // undefined
bar = 111;
console.log(bar); // 111
}
重新声明
使用 var
进行重复的变量声明不会触发错误,即使在严格模式下也不会,并且变量也不会丢失其值,除非声明具有初始化器。
var a = 1;
var a = 2;
console.log(a); // 2
var a;
console.log(a); // 2; not undefined
var
声明也可以与 function
声明在相同的作用域中。在这种情况下,var
声明的初始化器始终会覆盖函数的值,而与它们之间的相对位置无关。这是因为函数声明在任何初始化器被评估之前被提升,因此初始化器在后面出现并覆盖了该值。
var a = 1;
function a() {}
console.log(a); // 1
var
声明不能与 let
、const
、class
或 import
声明在相同的作用域中。
var a = 1;
let a = 2; // SyntaxError: Identifier 'a' has already been declared
由于 var
声明的作用域不是块,这也适用于以下情况
let a = 1;
{
var a = 1; // SyntaxError: Identifier 'a' has already been declared
}
它不适用于以下情况,其中 let
在 var
的子作用域中,而不是相同的作用域
var a = 1;
{
let a = 2;
}
函数体内的 var
声明可以与参数同名。
function foo(a) {
var a = 1;
console.log(a);
}
foo(2); // Logs 1
catch
块内的 var
声明可以与 catch
绑定标识符同名,但前提是 catch
绑定是简单的标识符,而不是解构模式。这是一种 已弃用的语法,您不应该依赖它。在这种情况下,声明被提升到 catch
块之外,但 catch
块内分配的任何值在外部不可见。
try {
throw 1;
} catch (e) {
var e = 2; // Works
}
console.log(e); // undefined
示例
声明和初始化两个变量
var a = 0,
b = 0;
使用单个字符串值分配两个变量
var a = "A";
var b = a;
这等同于
var a, b = a = "A";
注意顺序
var x = y,
y = "A";
console.log(x, y); // undefined A
在这里,x
和 y
在任何代码执行之前被声明,但赋值在后面发生。在 x = y
被评估时,y
存在,因此不会抛出 ReferenceError
,它的值为 undefined
。因此,x
被分配了 undefined 值。然后,y
被分配了值 "A"
。
多个变量的初始化
注意 var x = y = 1
语法 - y
实际上并没有被声明为变量,因此 y = 1
是 非限定标识符赋值,这在非严格模式下会创建一个全局变量。
var x = 0;
function f() {
var x = y = 1; // Declares x locally; declares y globally.
}
f();
console.log(x, y); // 0 1
// In non-strict mode:
// x is the global one as expected;
// y is leaked outside of the function, though!
与上面相同的示例,但使用严格模式
"use strict";
var x = 0;
function f() {
var x = y = 1; // ReferenceError: y is not defined
}
f();
console.log(x, y);
隐式全局和外部函数作用域
看起来像是隐式全局的变量可能是对外部函数作用域中变量的引用
var x = 0; // Declares x within file scope, then assigns it a value of 0.
console.log(typeof z); // "undefined", since z doesn't exist yet
function a() {
var y = 2; // Declares y within scope of function a, then assigns it a value of 2.
console.log(x, y); // 0 2
function b() {
x = 3; // Assigns 3 to existing file scoped x.
y = 4; // Assigns 4 to existing outer y.
z = 5; // Creates a new global variable z, and assigns it a value of 5.
// (Throws a ReferenceError in strict mode.)
}
b(); // Creates z as a global variable.
console.log(x, y, z); // 3 4 5
}
a(); // Also calls b.
console.log(x, z); // 3 5
console.log(typeof y); // "undefined", as y is local to function a
使用解构声明
每个 =
的左侧也可以是绑定模式。这允许一次创建多个变量。
const result = /(a+)(b+)(c+)/.exec("aaabcc");
var [, a, b, c] = result;
console.log(a, b, c); // "aaa" "b" "cc"
有关更多信息,请参阅 解构赋值。
规范
规范 |
---|
ECMAScript 语言规范 # sec-variable-statement |
浏览器兼容性
BCD 表格仅在启用 JavaScript 的浏览器中加载。