在函数作用域或全局作用域中通过关键字var声明的变量,无论实际上是在哪里声明的,都会被当成在当前作用域顶部声明的变量,这就是我们常说的提升(Hoisting)机制。下面以一个函数为例来说明:
function getValue(condition){
if(condition){
var value = "blue";
//其他代码
return value;
}else{
//此处可访问变量value,其值为undefined
return null;
}
//此处可访问value,其值为undefined
}
如果你不熟悉JavaScript,可能会认为只有当condition的值为true时才会创建变量value。事实上,无论如何变量value都会被创建。在预编译阶段,JavaScript引擎会将上面的getValue函数修改成下面这样:
function getValue(condition){
var value;
if(condition){
value = "blue";
//其他代码
return value;
}else{
return null;
}
}
变量value的声明被提升至函数顶部,而初始化操作依然留在原处执行,这就意味着在else子句中也可以访问到该变量,且由于此时变量尚未初始化,所以其值为undefined。
块级声明用于声明在指定块的作用域之外无法访问的变量。块级作用域(也称为词法作用域)存在于:
let声明的用法与var相同。用let代替var来声明变量,就可以把变量的作用域限制在当前代码块中。由于let声明不会被提升,因此开发者通常将let声明语句放在封闭代码块的顶部,以便整个代码块都可以访问。下面是let声明的实例:
function getValue(condition){
if(condition){
let value = "blue";
//其他代码
return value;
}else{
//变量value在此处不存在
return null;
}
//变量value在此处不存在
}
假设作用域中已经存在某个标识符,此时再使用let关键字声明它就会抛出错误,举例来说:
var count = 30;
//抛出语法错误
let count = 40;
但如果当前作用域内嵌另一个作用域,便可在内嵌的作用域中用let声明同名变量,如下:
var count = 30;
if(condition){
//不会抛出错误
let count = 40;
//更多代码
}
由于此处的let是在if块内声明了新变量count,因此不会抛出错误。内部块中的count会遮蔽全局作用域中的count,后者只有在if块外才能访问到。
ES6标准还提供了const关键字。使用const声明的是变量,其值一旦被设定后不可更改。因此,每个通过const声明的常量必须进行初始化。如下:
//有效的常量
const maxItems = 30;
//语法错误,常量未初始化
const name;
const与let声明的都是块级作用域,所以常量也只在当前代码块内有效,一旦执行到块外会立即被销毁。常量同样也不会被提升至作用域顶部,示例如下:
if(condition){
const maxItems = 5;
//更多代码
}
//此处无法访问maxItems
在这段代码中,在if语句中声明了常量maxItems,语句执行一结束,maxItems即刻被销毁,在代码块外访问不到这个常量。
与let相似,在同一作用域用const声明已经存在的标识符也会导致语法错误,无论该标识符是使用var(在全局或函数作用域中),还是let(在块级作用域中)声明的。
记住,const声明不允许修改绑定,但允许修改值。这也就意味着用const声明对象后,可以修改该对象的属性值。
const person={
name: "Nicholas"
};
//可以修改对象属性的值
person.name = "Gerg";
//抛出语法错误
person = {
name: "Greg"
};
与var不同,let和const声明的变量不会被提升到作用域顶部,如果在声明之前访问这些变量,即使是相对安全的typeof操作符也会触发引用错误,如下:
if(condition){
console.log(typeof value);//引用错误
let value = "blue";
}
由于console.log(typeof value)语句会抛出错误,因此用let定义并初始化变量value的语句不会执行。此时的value还位于JavaScript社区所谓的“临时死区”或TDZ中。但在let声明的作用域外对该变量使用typeof则不会报错,如下:
console.log(typeof value);//“undefined”
if(condition){
let value = "blue";
}
typeof是在声明变量value的代码块外执行的,此时value并不在TDZ中。这也就意味着不存在value这个绑定,typeof操作最终返回“undefined”。