- 说明:该文属于 大前端全栈架构白宝书专栏,目前阶段免费,如需要项目实战或者是体系化资源,文末名片加V!
- 作者:哈哥撩编程,十余年工作经验, 从事过全栈研发、产品经理等工作,目前在公司担任研发部门CTO。
- 荣誉:2022年度博客之星Top4、2023年度超级个体得主、谷歌与亚马逊开发者大会特约speaker、全栈领域优质创作者。
- 🏆 白宝书系列
- 🏅 启示录 - 攻城狮的自我修养
- 🏅 Python全栈白宝书
- 🏅 ChatGPT实践指南白宝书
- 🏅 产品思维训练白宝书
- 🏅 全域运营实战白宝书
- 🏅 大前端全栈架构白宝书
文章目录
- ⭐ let和const
- ⭐ let、const和var的区别
- ⭐ let、const的应用
⭐ let和const
let
和const
用来声明变量或声明常量
let
代替var,声明变量
const
,声明常量,const就是constant(恒定不变的)的缩写
示例代码:
// let 声明变量
let username = 'xiaoming';
// const 声明常量
const sex = '男';console.log(username, sex); // xiaoming 男
变量和常量的区别: 变量初始化之后,还可以重新赋值;常量一旦初始化,就不能重新赋值了,否则会报错。这句话也可以反过来理解,可以重新赋值的就是变量,不可以重新赋值的就是常量
。
我们初步了解了变量和常量的区别,但是我们仍然会有这样的疑问,为什么需要常量呢?什么时候需要声明常量?
- 首先我们先来看,为什么需要常量?
我们可以假设如果我们只有变量,在一些不能被修改的值一不小心被修改掉时,程序没有任何的提示和报错,比如一个人的性别被修改了也不会被发现:
const就是为了那些一旦初始化就不希望重新赋值的情况设计的
使用const的注意事项:
- 使用const声明常量,一旦声明,就必须立即初始化,不能留到以后赋值
- const声明的引用类型的常量允许在不重新赋值的情况下修改它
- 什么时候用const,什么时候用let?
一眼就能看出来是变量的,就直接使用let就行了,比如for循环里的循环变量。
如果不知道这个值会不会发生改变,可以先用const,当以后发现这个值需要改变时,再把const改成let
⭐ let、const和var的区别
let、const和var的区别可以总结为一下几点:
- 重复声明
- 变量提升
- 暂时性死区
- window对象的属性和方法(全局作用域中)
- 块级作用域(最重要的区别)
var | let、const | |
---|---|---|
重复声明 | 允许 | 不允许 |
变量提升 | 会 | 不会 |
暂时性死区 | 不存在 | 存在 |
window对象的属性和方法(全局作用域中) | 会自动变成window对象的属性或方法 | 不会自动变成window对象的属性或方法 |
块级作用域 | 没有块级作用域 | 有块级作用域 |
下面来我们敲几个demo来深入理解一下这些区别:
-
重复声明:
-
变量提升
虽然var会自行进行变量提升使得程序不报错,但我们在编程时还是要养成先声明后使用的编程习惯。
-
暂时性死区
只要作用域存在let、const,它们声明的变量或常量就自动“绑定”这个作用域了,不再受外部作用域的影响
暂时性死区和变量提升的影响很相似,只要我们养成了良好的编程习惯,就不会遇到暂时性死区的问题
-
window对象的属性和方法(全局作用域中)
全局作用域中,var声明的变量,通过function声明函数,会自动变成winddow对象的属性或方法;let、const声明的就不会。
- 块级作用域(最重要的区别)
首先,我们先要了解一下什么是作用域链。
我们的作用域有全局作用域
、函数作用域
、块级作用域
。
**块级作用域:**凡是带{}都是块级作用域,比如if(){}、for(){}、while(){}、do{}while()
**函数作用域:**function后面就是函数作用域,需要注意只有函数调用被的时候才会生成函数作用域,函数调用结束,函数作用域就销毁了
**全局作用域:**代码中的任何地方都能访问,其生命周期伴随着页面的生命周期
而作用域链就是内层作用域->外层作用域->…->全局作用域,形成的一个“链条”。程序在寻找变量/常量时就会按照这个”作用域链“进行寻找,如果找到了就使用这个变量,如果找不到就继续找,最终找到全局作用域中,如果全局作用域中也没有定义这个变量,就会报错了。
var没有块级作用域:
// var没有块级作用域
for (var i = 0; i < 2; i++) {// console.log(i);
}
console.log(i); // 2
let和const有块级作用域:
// let、const有块级作用域
for (let i = 0; i < 2; i++) {console.log(i);
}
console.log(i); // 报错
理解了“块级作用域“,我们再丰富一下上面的例子,使程序在寻找变量时形成一个“作用域链”:
let j = 10;
function func() {// let j = 100;for (let i = 0; i < 2; i++) {console.log(j); // 10}
}
func();
⭐ let、const的应用
案例:
我们先来做一个小案例,页面上有三个按钮,分别玮0号按钮、1号按钮和2号按钮,我们想要实现的功能是,点击哪个按钮就在控制台打印出对应的编号,效果如下:
我们可以用for循环,给每个按钮添加鼠标点击事件监听,当鼠标点击时,在控制台输出按钮编号。如果没有学习ES6,按照我们的常规思路写出来的代码是这样的:
为什么会出现上面的问题呢?因为上面的代码存在作用域问题,我们知道var是没有块级作用域的,而函数只有在被调用的时候才会形成函数作用域,在上面的代码中,事件监听函数里显然是没有i
变量的,根据作用域链,程序会继续往”外层“查找,因为var没有块级作用域,所以就直接找到了全局作用域,而在全局作用域中,i的值为3(因为程序运行后i的值就变成了3),所以不管点击哪个按钮,都会输出3。
上面代码中i
的作用域的图示如下:
那么如何来规避这个问题呢?我们可以把var替换成ES6中的let试试:
很显然,替换成let后作用域的问题被解决了,我们就实现了案例题目要求的效果。
上面代码中i
的作用域的图示如下:
那么不用ES6就真的无法解决这个问题了吗?聪明的程序员们怎么可能被这个问题打倒呢?我们还记得在学习js时学到过”闭包“,闭包拥有记忆性,当闭包产生时,函数所处环境的状态会被始终保持在内存中,不会在外层函数调用后被自动清除。那么利用闭包的这个特性,我们尝试改造一下第一次编写的代码:
上面代码中i
的作用域的图示如下:
了解闭包的会知道,闭包对性能是有”损伤“的,所以有了ES6,还是推荐大家使用ES6!