原文链接:http://javascript.info/var,translate with ❤️ by zhangbao.

在本教程最开始的时候,我们提到了变量,并提到有 3 种声明变量的方式:

  1. let

  2. const

  3. var

letconst 在词法环境的表现上完全一样。

var 是一种完全不同的熊孩子,它起源于非常古老的时代。它在现代脚本里通常不会被使用,但仍然潜藏在旧脚本里。

如果你不打算写这些脚本,你甚至可以跳过这一章或推看迟它,但之后它可能会咬你。

从第一眼看,var 的行为表现和 let 相似。那就是,声明一个变量:

  1. function sayHi() {
  2. var phrase = "Hello"; // 本地变量,用“var”而不是“let”声明
  3. alert(phrase); // Hello
  4. }
  5. sayHi();
  6. alert(phrase); // Error, phrase is not defined

但还是有区别的。

“var”没有块作用域

var 变量只属于函数和全局范围的,普通代码块 {...} 对它并不起作用。

例如:

  1. if (true) {
  2. var test = true; // 用“var”而不是“let”声明
  3. }
  4. alert(test); // true。在 if 之后,test 依旧可得

如果我们在第二行里使用了 let test,那么这个变量对 alert 就是不可见的。但是代码块对 var 并不起作用,所以 test 就成了一个全局变量。

这在循环里同样是奏效:var 变量并不是块或者一次循环迭代作用域的:

  1. for (var i = 0; i < 10; i++) {
  2. // ...
  3. }
  4. alert(i); // 10。“i”对循环可见,它是全局变量

如果代码块在一个函数里面的话,var 就成为了函数级别的变量:

  1. function sayHi() {
  2. if (true) {
  3. var phrase = "Hello";
  4. }
  5. alert(phrase); // 是可以的
  6. }
  7. sayHi();
  8. alert(phrase); // Error: phrase is not defined

可以看到,var 刺穿出 iffor 或者其他代码块。那是因为在很长时间,在 JavaScript 里时没有块词法环境的。var 就是一个怀旧的回忆。

“var”会被处理到函数开头

var 声明会被处理到函数开始(对脚本来说,就是全局环境的开始)。

也就是说,var 变量被定义在函数的开始,不管变量本身是在哪里定义的(假设定义不是在嵌套函数里)。

也就是代码:

  1. function sayHi() {
  2. phrase = "Hello";
  3. alert(phrase);
  4. var phrase;
  5. }

从技术上来讲,等同于将 var phrase 提升:

  1. function sayHi() {
  2. var phrase;
  3. phrase = "Hello";
  4. alert(phrase);
  5. }

甚至等同于下面的代码(记住,代码块会被 var 声明忽略):

  1. function sayHi() {
  2. phrase = "Hello"; // (*)
  3. if (false) {
  4. var phrase;
  5. }
  6. alert(phrase);
  7. }

人们称这种行为是“提升”,因为所有的 var 声明会被“提升”到函数顶部。

在上面的例子里,虽然 if (false) 分支没有执行,但是没有关系,其中的 var 还是被处理到函数的开始处了,因此在 (*) 处的时候,变量其实是已经存在的了。

声明会提升,但赋值不会提升。

最好用一个例子来证明,像这样:

  1. function sayHi() {
  2. alert(phrase);
  3. var phrase = "Hello";
  4. }
  5. sayHi();

var phrase = "Hello" 语句有两个操作:

  1. 变量声明 var

  2. 变量赋值 =

声明是在函数执行的开始开始处理(“提升”)的,但是赋值总是在它出现的地方才开始的。所以代码的工作原理是这样的:

  1. function sayHi() {
  2. var phrase; // 声明提升到开始...
  3. alert(phrase); // undefined
  4. phrase = "Hello"; // 而赋值则是执行到时才开始
  5. }
  6. sayHi();

因为所有的 var 声明都被提升到函数开始,所以我们可以在任何地方引用它们。但是变量在赋值之前一直是 undefiend

在以上两个例子中,alert 没有错误的运行了,因为变量 phrase 存在,但是它还没有被赋值,所以显示 undefined

总结

var 在行为表现上,主要有两点不同:

  1. 变量没有块作用域,它们最小的可见层是函数级别上。

  2. 变量声明是在函数开始时处理的。

还有一个与全局对象相关的小差异,我们将在下一章中讨论这个问题。

在大多数情况下,这些差异实际上是一件坏事。首先,我们不能创建块局部变量。而提升只是为错误创造了更多的空间。因此,对于新的脚本,var 很少使用。

(完)