var、let 用于声明变量,const 用于声明常量。都可以在一个语句中声明多个变量,变量以英文逗号分开。
var name1 = "Lee1",
age1 = 26,
job1 = "teacher1";
let name2 = "Lee2",
age2 = 26,
job2 = "teacher2";
const name3 = "Lee3",
age3 = 26,
job3 = "teacher3";
console.log(name1,age1,job1);
console.log(name2,age2,job2);
console.log(name3,age3,job3);
输出:
1、var关键字
存在变量提升:var 声明的函数及变量的声明都将被提升到函数的最顶部;变量可以先使用再声明,但变量的值并不会提升。
console.log(num)//undefined
var num=20;
这里输出 num 为 undefined:1、因为变量提升,所以并不会报错 (ReferenceError);2、因为变量的值不会提升,所以输出的是默认值undefined。
2、let关键字
1)使用let关键字声明的变量具有块级作用域:由 { } 包括,if语句和for语句里面的{ }也属于块作用域,for循环中( ) 中用let 声明的变量与此for循环绑定,在for循环外访问不到。
for(let i=0;i<2;i++){
console.log(i);
}
console.log("----")
console.log(i);
输出:
2)使用let关键字声明的变量不存在变量提升。
console.log(n);
let n=10;
输出:
3)使用let关键字声明的变量具有暂时性死区特性:在块级作用域内声明变量,这个变量会和这个块级作用域绑定
var num=10;
if(true){
console.log(num); //这里会报错,
//因为在块级作用域声明变量,这个变量会和这个块级作用域绑定,这里与外面的num无关了。
let num =20;
}
输出:
4)不允许重复声明:let 不允许在相同作用域内重复声明同一个变量。
function foo(){
let a=0;
var a=1;
}
foo();
输出:
3、const关键字
作用:声明常量(内存地址不能变化的量)。
1)具有块级作用域
2)不存在变量提升。
3)具有暂时性死区特性。
4)声明常量必须赋值。
5)常量赋值后,值不能更改。——对于复杂类型数据,它内部的值是可以更改的,但是不能对它重新赋值,即数据值本身不能更改,因为重新赋值更改了该数据的内存地址。
const num=20;
num=10;
console.log(num);
输出:
const obj={
name:'刘亦菲',
age:18
}
obj.name='赵灵儿'
console.log(obj);
obj={
name:'李逍遥',
age:21
}
console.log('------');
console.log(obj);
输出:
这里的‘------’都未输出,是因为到 obj 赋新值时就报错了。
6)如果存储的关键字不需要变化,尽量使用 const 关键字,比如函数定义等。因为 const 关键字声明的值不需要变化,JavaScript 解析引擎不需要实时监控值的变化,所以 const 比 let 效率高。
总结:
var |
let |
const |
函数级作用域 |
块级作用域 |
块级作用域 |
变量提升 |
不存在变量提升 |
不存在变量提升 |
值可更改 |
值可更改 |
值不可更改 |
没有暂时性死区特性 | 具有暂时性死区特性 | 具有暂时性死区特性 |
声明时不必赋值 | 声明时不必赋值 | 声明时必须赋值 |
经典面试题
var arr=[]
for(var i=0;i<2;i++){
arr[i]=function(){
console.log(i);
}
}
arr[0](); //2
arr[1](); //2
分析:在函数内部没有定义变量 i,函数执行时在自己的作用域找不到变量 i 值,根据作用域链查找原则,要向上一层作用域查找,这里的上一层作用域就是全局作用域,有i。函数执行时,循环早已结束,所以 i 的值是不满足循环条件的值,即 i = 2,所以函数执行输出的是2。
let arr=[]
for(let i=0;i<2;i++){
arr[i]=function(){
console.log(i);
}
}
arr[0](); //0
arr[1](); //1
分析:每次循环都会产生一个块级作用域,每个块级作用域中的变量都不同,函数执行时输出的是自己上一级(循环产生的块级作用域)作用域下的 i 值。