本文所讲的内容对于帮助理解旧脚本很有用。
但这不是我们编写新代码的方式。
在本教程最开始那部分的 变量 这章中,我们提到了变量声明的三种方式:
letconstvar
var 声明与 let 相似。大部分情况下,我们可以用 let 代替 var 或者 var 代替 let,都能达到预期的效果:
var message = "Hi";
alert(message); // Hi
但实际上 var 却是一头非常不同的,源自远古时代的怪兽。在现代脚本中一般不再使用它,但它仍然潜伏在旧脚本中。
如果你不打算接触这样的脚本,你甚至可以跳过本章或推迟阅读本章。
另一方面,了解将旧脚本从 var 迁移到 let 时的区别,以避免奇怪的错误,是很重要的。
“var” 没有块级作用域
用 var 声明的变量,不是函数作用域就是全局作用域。它们在代码块外也是可见的(译注:也就是说,var 声明的变量只有函数作用域和全局作用域,没有块级作用域)。
举个例子:
if (true) {
var test = true; // 使用 "var" 而不是 "let"
}
alert(test); // true,变量在 if 结束后仍存在
由于 var 会忽略代码块,因此我们有了一个全局变量 test。
如果我们在第二行使用 let test 而不是 var test,那么该变量将仅在 if 内部可见:
if (true) {
let test = true; // 使用 "let"
}
alert(test); // ReferenceError: test is not defined
对于循环也是这样的,var 声明的变量没有块级作用域也没有循环局部作用域:
for (var i = 0; i < 10; i++) {
var one = 1;
// ...
}
alert(i); // 10,"i" 在循环结束后仍可见,它是一个全局变量
alert(one); // 1,"one" 在循环结束后仍可见,它是一个全局变量
如果一个代码块位于函数内部,那么 var 声明的变量的作用域将为函数作用域:
function sayHi() {
if (true) {
var phrase = "Hello";
}
alert(phrase); // 能正常工作
}
sayHi();
alert(phrase); // ReferenceError: phrase is not defined
可以看到,var 穿透了 if,for 和其它代码块。这是因为在早期的 JavaScript 中,块没有词法环境,而 var 就是这个时期的代表之一。
“var” 允许重新声明
如果我们用 let 在同一作用域下将同一个变量声明两次,则会出现错误:
let user;
let user; // SyntaxError: 'user' has already been declared
使用 var,我们可以重复声明一个变量,不管多少次都行。如果我们对一个已经声明的变量使用 var,这条新的声明语句会被忽略:
var user = "Pete";
var user = "John"; // 这个 "var" 无效(因为变量已经声明过了)
// ……不会触发错误
alert(user); // John
“var” 声明的变量,可以在其声明语句前被使用
当函数开始的时候,就会处理 var 声明(脚本启动对应全局变量)。
换言之,var 声明的变量会在函数开头被定义,与它在代码中定义的位置无关(这里不考虑定义在嵌套函数中的情况)。
那么看一下这段代码:
function sayHi() {
phrase = "Hello";
alert(phrase);
var phrase;
}
sayHi();
……从技术上讲,它与下面这种情况是一样的(var phrase 被上移至函数开头):
function sayHi() {
var phrase;
phrase = "Hello";
alert(phrase);
}
sayHi();
……甚至与这种情况也一样(记住,代码块是会被忽略的):