写在前面:在ES6发布多年后,你会发现let 和const越来越多的进入到你的视野,如何认识并使用它们是我们必须要掌握的基础知识
目录
3.2、和let一样都具有 块级作用域的特点、在同一作用域不能多次声明。
0. var、let、与const大致区别
作用域 | 声明初始化的不同 | 独有特点 | |
var | 使用函数作用域 | 同一执行上下文可以重复声明 同一执行上下文可以重复赋值 可以先声明再赋值 | 变量提升 |
let | 使用块级作用域 | 同一执行上下文不可以重复声明 同一执行上下文可以重复赋值 可以先声明再赋值 | |
const | 使用块级作用域 | 同一执行上下文不可以重复声明 同一执行上下文不可以重复赋值 不可以先声明再赋值,必须同时声明并赋值 | 不能重复赋值,但对象的键不受限制 |
1. var 作为函数作用域声明
1.1 var的函数作用域
用var声明一个变量,变量会被添加到最接近的执行上下文。所以在函数中声明,会被添加到函数的执行上下文中,如果变量没有声明就已经初始化了,那么它会自动被添加到全局上下文中(全局上下文指的就是window对象);
function demo(a,b){
var sum=a+b
}
demo(1,2)
console.log(sum) //sum is not defined
在demo()被调用时,声明了变量sum,sum被添加到最近的执行上下文,成为了demo函数中的局部变量。在函数执行完成后,在全局中自然是无法访问到demo函数的局部变量的
function test(){
a=100
}
test()
console.log(a) //100
由于调用test()后,a被添加到了全局上下文,在函数执行完成后依然可以访问;但是请注意,此种方式“未声明就开始初始化变量”是一个非常常见的错误,会导致很多问题。在严格模式下此种方式会报错。
1.2 变量提升
用var 声明的变量具有变量提升的特性,函数内var声明的所有变量在函数体内始终是可见的 。请看下面的例子
var num=12345;
function test(){
console.log(num); //打印`undefind`
var num=789;
console.log(num); //打印`789`
}
test();
看到上面的代码,很多同学会说:“第二次打印的结果我知道,第一次怎么不是12345?怎么不打印上面的全局变量12345 ”
实际上,根据我们提到的:函数内声明的所有变量在函数体内始终是可见的 也就是说我们在第四行代码声明的局部变量被放在了最前面,也就是如下的形式:
var num=12345;
function test(){
var num //声明被提前到顶部 在这里声明了局部变量 但未赋值
console.log(num); //打印`undefind` 在这里打印的就是上面的局部变量
num=789; //在这里将num初始化并赋值
console.log(num); //打印`789`
}
test();
因此,为了写出清晰且真实的变量作用域,请保证良好的开发习惯,请将变量声明放在函数体顶部。
2. 使用let的块级作用域声明
2.1 let的块级作用域
let 声明是在ES6中新增的,他的作用域是块级的,在引入let关键字前,javascript是没有块级作用域概念的(es6前是函数作用域)。块级作用域是由最近的一对 { } 包裹的,下图这些都是块级作用域
if(true){
let a
}
console.log(a); // a is not defined
let num=10
while (num<10) {
let a
}
console.log(a); // a is not defined
function test(){
let a
}
console.log(a); // a is not defined
{
let a //这并不是对象字面量,而是独立的块
}
console.log(a); // a is not defined
2.2 let不能在同一作用域多次声明
let不能在同一作用域多次声明,var会默认覆盖
var _num=1
var _num=2
console.log(_num) // 2
let _string='nihao'
let _string='hellow' //错误警告:已经声明过
console.log(_string)
2.3 let的最佳应用 使用let声明的变量迭代
for(var i=0;i<10;i++){}
console.log(i) //10
for(let j=0;j<10;j++){}
console.log(j) //j is not defined
“严格来讲,let在javascript运行时也会被提升,但由于 暂时性死区 的缘故,实际上不能在声明前使用let变量。”
3. 使用const的常量声明
const也是在ES6中新增的,其特点是:
3.1、使用const声明的变量必须同时初始化和赋值 不能使用null占位。
const A // 常量声明未赋值
A=10
const B=10 // 常量声明未赋值
console.log(B) // 10
3.2、和let一样都具有 块级作用域的特点、在同一作用域不能多次声明。
3.3、const在它的生命周期任何时候都不能再次赋值:
let obj={}
obj=12345
console.log(obj) //12345
const OBJ2={}
OBJ2={} //obj2已被赋值,不能再次赋值
OBJ2=12345 //obj2已被赋值,不能再次赋值
虽然说使用const声明的对象不能被再次赋值,但是如果赋值的是对象,我们是可以更改对象的键值对的;但是被Object.freeze()包裹的对象会默认失败!
const OBJ={}
OBJ.name='qin'
console.log(OBJ.name) //qin
const OBJ=Object.freeze({})
OBJ.name='qin'
console.log(OBJ.name) //undefined
由于const的值是单一的,并且不可修改,v8引擎将const声明的所有实例都替换成了实际的值,不用在查询表中进行变量查询;
所以在实际开发中,能使用const声明尽量使用const声明,除非你这个变量需要重新赋值;其次为了保持良好的开发习惯,我们一般将常量都使用大写
注释:本文参考《JavaScript高级程序设计 第四版》 Matt Frisbie著