简介
好像又很久没有写文章了,说实话,写文章这种东西是真的看心情,有时候想写,有时候又不想写,就和过山车差不多哈哈哈,今天我们也不讲深奥的,就讲讲我们在变量声明时很常见的 let 和 const。
既然讲到 let 和 const,我们就得摸着自己的良心问一下自己,这是什么,为啥出现,它为我们解决了什么样的问题。再此之前,让我们看看在 ES6 之前 var 到底遗留了什么问题给我们。
var 迷惑操作锦集
同一作用域可重复声明变量
var a = 1;
var a = 2;
console.log(a);//2
//可以理解成这样
var a;
a = 1;
a = 2;
console.log(a);
对于这段代码,有人想发表什么意见不。没有啊,我觉得没什么问题啊,很好啊。emmmmm,好像是没什么问题哦,但是当我们需要外部加载多个js脚本的时候,才会突然发现,咦,我这个变量的值怎么和我心里想的不一样,然后开始找bug,抓头发,花了1分钟,一个小时,或者是一天,才知道原来是其他的js脚本也声明了这个变量,把我的变量值给偷偷的改掉了。这还是在js脚本是自己写的情况下,要是其他人写的呢,不得把我坑死。这时候我们才会开始吐槽,为啥声明相同变量浏览器不给我报错啊!心疼自己头发几分钟。
变量提升带来的问题
奇怪的数据访问方式
if(Math.random() < 0.5){
var a = 1;
console.log(a);
}else{
var a = 2;
console.log(a);
}
console.log(a);
//可以理解成这样
var a;
if(Math.random() < 0.5){
a = 1;
console.log(a);
}else{
a = 2;
console.log(a);
}
console.log(a);
//另一个例子
console.log(b);//undefined
var b = 1;
//也可以理解成这样
var b;
console.log(b);
b = 1;
眼尖的小伙伴会说,为啥外面的 console 能读取到变量 a哦,我明明没有在外面声明变量 a,我还蛮期待它会报错的呢。???还有就是为啥能在 b变量声明前就能读到 b啊,这不合常理。
闭包问题
for(var i = 1; i <= 10; i++ ){
var btn = document.createElement("button");
btn.innerText = "按钮" + i;
btn.onclick = function(){
console.log(i);
}
document.body.appendChild(btn);
}
console.log(i);//11
//也可以这样理解
var i;
var btn;
for(i = 1; i <= 10; i++ ){
btn = document.createElement("button");
btn.innerText = "按钮" + i;
btn.onclick = function(){
console.log(i);
}
document.body.appendChild(btn);
}
这段代码就需要你们在自己的编辑器上运行了,你会发现问题的,嘿嘿。
污染全局对象成员
var a = 1;
console.log(window.a, window.a === a);//1 true
var console = 1;
console.log(console);//console.log is not a function
直接报错了,还有这种操作,妙啊妙啊。
上面奇奇怪怪的问题大家应该都知道原因吧,不是吧不是吧,都2021年了,不会有人还不知道把。没有贬义的意思,单纯调侃一下。实在不知道的,可以自己百度搜索一下,随便锻炼你们的动手能力(当然有一部分自己懒的原因,我反思一下),而且今天的重点是 let 和 const。
没错,他们的出现解决了以上几个问题。
let
这是取自于 MDN(Mozilla Developer Network) 中对 let 的一段描述。是不是有点看不懂,其实说白了就是 let 关键字可以将变量绑定到所在的任意作用域中(通常是 { .. } 内部)。换句话说,let 为其声明的变量隐式的绑定了所在的块作用域。怎么理解呢,用之前的代码来解释把。
if(Math.random() < 0.5){
let a = 1;
console.log(a);
}else{
let a = 2;
console.log(a);
}
console.log(a);//a is not defined
这个时候你会发现外面的 console 已经读取不到变量 a了,为啥呢,因为变量 a被困在在这个{ .. }里面了,俗称块级作用域,它不会再提升到外面的全局作用域中。
总结:let声明的变量只在其声明的块或子块中可用,这一点,与var相似。二者之间最主要的区别在于var声明的变量的作用域是整个封闭函数。
用这个知识再来讲讲上面的闭包问题
//在没有let之前,可以用立即执行函数来解决
for(var i = 1; i <= 10; i++ ){
var btn = document.createElement("button");
btn.innerText = "按钮" + i;
(function(i){
btn.onclick = function(){
console.log(i);
}
})(i);
document.body.appendChild(btn);
}
//用let解决更方便
for(let i = 1; i <= 10; i++ ){
let btn = document.createElement("button");
btn.innerText = "按钮" + i;
btn.onclick = function(){
console.log(i);
}
document.body.appendChild(btn);
}
console.log(i);//i is not defined
不知道看到这里,你们有没有动手实践过上面的例子,现在我不装了,我摊牌了。之前的结果是不过你点哪个按钮,打出的结果都是11,为啥呢,为啥呢?现在就容许我,这个前端新手给你们解释一下。
因为当我们点击按钮触发点击事件的时候,里面的循环早已经执行完了,由于 var声明的变量提升问题,执行点击函数的时候,读取到的变量 i其实就是全局环境下的变量 i,而这时候的 变量 i 一直都是11,所以无论你点哪个按钮,打印出来的都是11。那为啥 let声明的变量就可以解决这个问题呢?那是因为每进入一次新的循环体,let 声明的变量 i都会隐式的绑定该循环体中的作用域,这样不同的按钮就都会有各自独自的块级作用域,就很好的解决了上面奇奇怪怪的问题。
至于 let不允许在同一作用域下声明相同变量,不会污染全局对象成员,这个相对来说就比较简单,就真的靠你们自己自觉动手了啊。
不过还有一个比较有意思的例子如下:
function do_something() {
console.log(foo); // ReferenceError: Cannot access 'foo' before initialization
let foo = 2;
}
do_something();
通过上面的讲解,我们得知,let声明的变量不会提升,所以报错的话那也很正常,不过这个报错的信息怎么和我们想的不一样,不应该是foo is not defined嘛,但这里的报错却是Cannot access 'foo' before initialization,意思就是不能在初始化前访问 foo变量。其实这里又会有一个新的知识点,那就是暂时性死区,实际上foo变量声明还是提升了,只不过被放入了暂时性死区,当访问暂时性死区里面的变量,就会报上面的错误,而只有声明了该变量,才能从暂时性死区里面拿出来,进行正常访问。在MDN中是这么说的:
const
const 和 let基本有一样的效果,唯一的区别只是 const声明的变量必须在初始化阶段就进行赋值操作,并且不能被重复赋值。这意味了 const一般用来声明一个常量。
const a;//SyntaxError: Missing initializer in const declaration
const b = 1;
b = 2;//TypeError: Assignment to constant variable.
但是这里要注意一下,这里说的不能改变指的是声明的变量所持有的值是不可变的,如果持有的值是一个引用对象地址,那么该引用对象内部的内容还是可以改变的。如下:
const obj = {
name: "xxx",
age: 18,
}
obj.name = "aaa",
console.log(obj);//{name: "aaa", age: 18}
结语
今天到这里就讲完了,没想到我们平时这么常见的声明语句也有这么多学问在里面,那么今天就到这里啦,谢谢各位品读,下次光临。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!