在JS中定义变量有3个标识符,即var、let与const,其中let与const是ES6标准中引入的。在ES5及以下版本,JS作用域只有全局作用域与局部作用域,而在ES6中引入了块状作用域(主要是针对let与const而言,而var不受此限)。
一、三种作用域定义
全局作用域,是指window对象作用范围内所属,也就是<script>标签内部和JS脚本文件中定义的变量。在全局作用域中定义的变量、属性及方法为window对象调用,在此定义的变量为浏览器页面所创建,生命周期为页面存续期间。
局部作用域,是指在函数体内所属,包括有名函数体、匿名函数体和箭头函数体内定义的变量。在此定义的变量,为函数体对象调用函数体期间创建,当执行完成后变量随函数体对象一起销毁,不复存在。
块状作用域,是指在字符“{”和“}”中之间区域,包括除函数体{}外的所有其他{}间,如if{}、else if{}、else{}、for{}、while{}、switch{}、do{}以及单独{}等。此作用域对var来说,不起作用,其内的变量作用范围由当前{}的上一级作用域决定,隶属于上一级作用域。但对let和const而言,当前{}就相当于其上一级作用域的下一级作用域,以它们定义的变量只能在当前{}中使用,外面不能使用。
二、三种变量标识符用法理解
(一)var变量标识符
1.var由于提升机制,在整个作用域内都可调用
用var定义的变量(如果不指定标识符默认为var定义),无论其是在当前作用域(指全局或局部作用域)的哪里声明的,在JS预编译时都会将其提升到当前作用域的顶部,这即所谓的Hoisting提升机制。
function getValue(condition){
console.log("作用域顶部"+value);//输出结果:作用域顶部undefined
if(condition){
var value="blue";
console.log("if"+value);//输出结果:ifblue
}else{
console.log("else"+value);//由于conditon为真,此处无输出
}
console.log("作用域尾部"+value);//输出结果:作用域尾部blue
};
getValue(true);
由于var提升机制,块状作用域对var不起作用,其处于函数体作用域,因此对于if(condiion)是否为true,都将创建var,相当于在if块状体前就创建了var,只不过没有赋值,为undefined状态。
2.var可以使用上一级作用域变量,不能使用下一级作用域内变量
var top="上一级变量";
function show(){
var buttom="下一级变量";
console.log(top);//输出结果:上一级变量
}
show();
console.log(buttom);//输出结果:出现错误,提示buttom is not defined
3.var在不同作用域定义相同变量名,是具有不同的取值作用
var msg="全局作用域msg";
function show(){
var msg="局部作用域msg";//当局部作用域有msg时,就取局部作用域的变量,没有时再取上一级相同变量
console.log(msg);//输出结果:局部作用域msg
}
show();
console.log(msg);//输出结果:全局作用域msg
4.var是可以在当前作用域中重复定义(或多次赋值)的,只取最后一次定义(赋值),
var msg="第一次定义msg";
var msg="第二次定义msg";
console.log(msg);//输出结果:第二次定义msg
msg="改变msg值";
console.log(msg);//输出结果:改变msg值
(二)let变量标识符
1.let变量不存在提升机制,因此要注意定义及访问位置
function show(){
//在定义之后访问
let one="第一个变量";
console.log(one);//输出结果:第一个变量
//在定义之前访问
console.log(two);//输出结果:出现错误,提示Cannot access 'two' before initialization
let two="第二个变量";
}
show();
2.let变量不能进行重复定义,也不能与var变量名相同,将抛出错误
//var与let不能定义相同名变量
var msg="用var定义的msg";
let msg="用let定义msg";
console.log(msg);//输出结果:抛出错误,提示Identifier 'msg' has already been declared
//let不能重复定义
let msg="用let第一次定义msg";
let msg="用let第二次定义msg";
console.log(msg);//输出结果:抛出错误,提示Identifier 'msg' has already been declared
3.let变量可多次赋值,取最后一次赋值结果
let msg="定义msg";
msg="改变msg值1";
msg="改变msg值2";
console.log(msg);//输出结果:改变msg值2
4.let变量的临时死区(TDZ)特性
所谓临时死区(TDZ)特性,是块状作用域的独有特性。由于块状作用域是针对let和const声明的,而不针对var。如果也让块状作用域中的let、const变量也象var将它们提升到上一级作用域,当块状作用域完成执行后,还保存在内存中就会造成内存浪费及上一级作用域再定义相同变量时出现let、const重复定义错误。因而,在块状作用域中,var变量实际上是提升至块状作用域的上一级中,而let、const则放在块状态作用域专门开辟的死区(TDZ)中,当块状作用域完成执行后,它的死区(TDZ)随即销毁。对其理解,请看下面两个实例。
//当在块状态作用内访问未先定义的let变量会报错
if(true){
console.log(typeof value);//输出结果:报错,提示Cannot access 'value' before initialization
let value="blue";
}
//而当在块状态作用外,访问未先定义的变量不会报错,系统会默认创造一个var类型的变量,只是未初始化
console.log(typeof value);//输出结果:undefined
if(true){
let value="blue";
}
5.let变量在全局作用域中的特性
let和const与var在全局作用域中的是不一样的,var在全局作用域时,它会创建一个新的全局window对象的属性,这意味着用var很可能会无意中覆盖一个已经存在的全局属性(相同变量名)。
//var变量在全局作用域中定义,可会覆盖有相同变量名的变量
var RegExp="Hello!";
console.log(window.RegExp===RegExp);//输出结果:true
//let变量在全局作用域中定义,不会覆盖有相同变量名的变量
let RegExp="Hello!";
console.log(window.RegExp===RegExp);//输出结果:false
换句话说,在全局部作用域中,使用let、const定义变量的话,如域有相同变量名的变量,不会进行覆盖,只是创建一个新的绑定,只能是拦截遮蔽它,让它指向新的地址,而不会覆盖它。
(三)const变量标识符
1.具有上述let的1、2、4、5特性,可参照举例
2.const变量不能重复赋值
if(true){
const PI=3.14;
PI=3.1415;//出现报错,提示Assignment to constant variable.
console.log(PI);//由于前句出现报错,因此此句无执行
}