定义变量的3个关键字——var、let和const,还可以通过与词法环境的关系将其进行分类(换句话说,按照作用域分类)。可以将var分为一组,let与const分为一组。
使用关键字var
当使用关键字var时,该变量是在距离最近的函数内部或是在全局词法环境中定义的。(注意:忽略块级作用域)这是JavaScript由来已久的特性。
console.log("----------------------使用关键字var----------------------");
var globalNinja = 'Yoshi';//使用关键字var定义全局变量
function reportActivity() {
//使用关键字var定义函数内部的全局变量
var functionActivity = "jumping";
for (var i = 1; i < 3; i++) {
//使用关键字var在for循环中定义两个变量
var forMessage = globalNinja + " " + functionActivity;
//在for循环中可以访问块级变量,函数内的局部变量以及全局变量
if (forMessage === 'Yoshi jumping') {
console.log("Yoshi is jumping within the for block");
}
console.log("Current loop counter:" + i);
}
//在for循环外部,仍然可以访问for循环中定义的变量
if (i === 3 && forMessage === 'Yoshi jumping') {
console.log("Loop variables accessible outside of the loop");
}
}
reportActivity();
//函数外部无法访问函数内部的局部变量
if (typeof functionActivity === 'undefined' && typeof i === 'undefined' && typeof forMessage === 'undefined') {
console.log("We can not see function variables outside of a function.");
}
我们首先定义全局变量globalNinja,接着定义函数reportActivity,在该函数中使用循环并验证变量globalNinja的行为。可以看出,在循环体内可以正常访问块级作用域中的变量(变量i与forMessage)、函数体内的变量(functionActivity)以及全局变量(globalNinja)。
//但是JavaScript中特殊的并使得许多从其他语言转向JavaScript的开发者们困惑的是,即使在块级作用域内定义的变量,在块级作用域外仍然能够被访问:
if (i === 3 && forMessage === 'Yoshi jumping') {
console.log("Loop variables accessible outside of the loop");
}
这源于通过var声明的变量实际上总是在距离最近的函数内或全局词法环境中注册的,不关注块级作用域。下图展示了reportActivity函数内的for循环执行后的词法环境。
通过var声明的变量,在距离最近的函数内 或全局词法环境中定义(忽略块级作用域)。变量forMessage与i虽然是被包含在for循环中,但实际上是在reportActivity环境中注册的(距离最近的函数环境)
上述代码中展示了3种词法环境
1.变量globalNinja是在全局环境中定义的(距离最近的函数内或全局词法环境)。
2.reportActivity函数创建的函数环境,包含变量functionActivity、i与forMessage,这3个变量均是通过关键字var定义的,与它们距离最近的是reportActivity函数。
3.for循环的块级作用域,关键字var定义的变量忽略块级作用域。
使用let与const定义具有块级作用域的变量
var是在距离最近的函数或全局词法环境中定义变量,与var不同的是,let和const更加直接。
let和const直接在最近的词法环境中定义变量(可以是在块级作用域、循环内、函数内或全局环境内)。可以使用let和const定义块级别、函数级别、全局级别的变量。
console.log("----使用const与let关键字-------------");
//使用const定义全局变量,全局静态变量通常使用使用大写表示
const GLOBAL_NINJA = 'Yoshi';
function reportActivity() {
//使用const定义函数内的局部变量
const functionActivity = "jumping";
//在for循环中,我们毫无意外的可以访问块级变量,函数变量和全局变量
//使用let在for循环中定义两个变量
for (let i = 1; i < 3; i++) {
let forMessage = GLOBAL_NINJA + " " + functionActivity;
if (forMessage === 'Yoshi jumping') {
console.log("Yoshi is jumping within the for block");
}
console.log("Currrent loop counter:" + i);
}
//在for循环外部无法访问for循环内的变量
if(typeof i === 'undefined' && typeof forMessage === 'undefined') {
console.log("Yoshi is jumping within the for block");
}
}
reportActivity();
//自然地,在函数外部无法访问任何一个函数的内部变量
if (typeof functionActivity === 'undefined' && typeof i === 'undefined' && typeof forMessage === 'undefined') {
console.log("We can not see function variables outside of a function.");
}
reportActivity函数内的for循环执行完成之后的词法环境。此时我们仍然可以看到3个词法环境:全局环境(函数和块级作用域之外的全局代码)、reportActivity函数循环和for循环体。但是由于我们使用了关键字let和const,那么变量则是在距离最近的词法环境中定义的:变量GLOBAL_NINJA是在全局环境中定义的,变量functionActivity是在函数reportActivity中定义的,变量i与forMessage是在for循环的块级作用域中定义的。
当使用let与const声明变量时,变量是在距离最近的环境中定义的。在本例中,变量forMessage与i是在for循环的块级作用域中定义的,变量functionActivity是在函数reportActivity中定义的,变量GLOBAL_NINJA是在全局环境中定义的。
参考《JavaScript忍者秘籍》