js是弱类型语言,语法很松散,这是一个缺点。
之前,在js中变量声明:var。
为了解决这个缺点,js中声明变量新增两个语法:let、const
var
特点有三:
- 变量声明提升、
- 可重复定义同一个名字的变量不报错、
- 全局变量挂载到window
let和const的特性:
- 没有变量声明提升
- 不能重复声明同一个变量,即使是var过的变量,甚至是行参也不行。
- 声明的变量不会挂载到window上
- 有块级作用域的特点
- 在大括号{}里边会形成临时死区
- 可解决闭包问题。
- [const特殊] var和let定义变量,const定义常量
let和const的特性详解:
let/const声明的变量不会进行变量声明提升:
let/const重复声明一个变量报错。 如果这个变量已经被var过了,也不能再继续let/const定义了:
延伸到函数的行参上:
函数行参在函数内部也是相当于var了一个变量的存在。
用var定义变量覆盖行参:
但是如果在函数内部用let声明一个和行参同名的变量,就会报错:
let/const声明的变量不会挂在到window上,var会(容易造成混乱和冲突,window上的原有属性不能随便改)。
let/const加强对作用域的控制:
作用域就是变量的生命周期,或者说变量在哪里能够被使用。
也就说let/const能让变量的生命周期更精准、更规范。(具体如何控制?)
当let/const配合{}使用时能产生块级作用域。
换句话说,只要有大括号,在大括号中let/const一个变量,该变量的生命周期就是这个大括号。
对比var 的效果
块级作用域嵌套:
外部父块级作用域定义的变量,内部子块级里能获取到。
也就是说虽然产生块级作用域,但是在里边还是能看到外边的,在块级里能看到全局,在子块级里能看到父块级。这就是临时死区【Temporal Dead Zone】
临时死区【Temporal Dead Zone】
只要一个变量在大括号内用let/const声明了,那这个let/const声明就在整个块级里边称霸了,
以上,如果子块里边没有let/const声明一个和父级同名的变量,那将相安无事。子块级的变量使用还是会去父块级或全局中去找。
如果给子块级“胆子”,声明了和父级中已有的同名变量,那他就敢“造反”,整个子块中的这个同名变量他说了算。此时如果在子块范围内、let/const声明之前使用这个变量,就会报错。
let/const声明其他和父块级或全局变量不重名的变量倒没有关系。
【const特殊】const定义的常量不能修改,let和var可以。
总结特性:
他其实跟var还是挺像的,只不过var的是局部函数作用域、let和const是局部块级作用域;var的变量声明提升,let和const的变量声明不提升且报错而已。
例题:
代码语言:javascript复制{
let test = 1;
console.log(test);
{
console.log(test);
let test = 2;
{
let test = 3;
}
}
}
CONST详解:
CONST:常量声明。定义一个常量。这个常量不能被改变。
有以下特点需要注意
- const 变量名 时,后边必须要赋值。否则之后再怎么操作都会报错。
- const定义的常量不能修改,表面上看是原始值不能修改,引用类型值可以修改。但实际上指的是常量地址不能修改。
- CONST拥有let拥有的一切特性。所以在实际应用中第一位的要用const,第二位再用let。
- const还有性能上的优点,声明常量后 ,浏览器就不用追踪变量的变化,节省性能。
- 对于开发人员来说,如果修改常量或者命名冲突会被报错而不是直接覆盖,减少出错率。
const特点详解 :
const声明变量时,必须立即赋值:
所以,需要先实现声明一个空变量,且后期会动态改变此变量的情况,就得考虑用let声明。
const定义的常量,不能再做修改:
const定义的常量,在栈内存的地址不能修改。
但是const定义的引用类型值的常量,其属性值或项还是能改:
不过,略微修改结果又变得和原始值一样:
修改const常量的指针会报错。但是修改堆空间的属性值却没有关系。
存储常量的空间里边的值不能发生改变。存储常量的空间在栈空间,也就是栈里边存放的值不能改变。不代表堆内存里边的值不能改变。
const定义的常量让浏览器很省心、让开发人员也很省心:
const的效率可能比var和let就高一点,因为很多业界大牛猜测,浏览器中会对const变量少了一些追踪,
不像let和var要时刻去检测它有没有变化。一旦监控就需要一些算力。但是const声明后就不用去检测了,
所以无论从提升效率还是降低bug出现概率上看,都应该先使用const。
const结语:
能用const就用const。虽然babel转换后他还是var。
延展~在使用了babel转换工具后,let和const的一些表现会转化成什么?
在声明后重新赋值这方面观察:
可以看到,let和const都被转换成了var。但是他们的特点还是被曲线救国的招式给保留了:
let声明的变量,在此修改,没有问题。转换成var以后作用一致。
但是const就不一样了,const声明的是常量,转成var后不会有这个功能,但是babel创建了一个_readOnlyError的内部报错对象,监测到const常量被重新赋值后就调用该函数向控制台抛出了一个错误以提示开发者。
在变量声明提升的方面观察:
遗憾的是,let转换后变成var,有了变量提升,转换前(左边)直接运行是肯定会报错的,转换后打印undefined,说明变量提升生效了。babel没有对这一点进行处理。
在暂时死区(块级作用域)方面观察:
在块级里边的引用也被处理了(console部分)。
这一次babel确实处理了,是将大括号里边用let声明的变量名加了个下划线,以和块级外边做了区分,一样达到了在外边变量会报错的结果。
但同样遗憾的是,对于全局作用域内声明的变量会挂在到window上这一点,依旧没有做防御工作。
babel编译后的变量还是会挂在到window对象上。
最后对于经典题的观察:
可见,利用let解决的异步回调里引用循环后的全局变量问题,同样也是闭包的原理实现的。